Merge pull request 'TCP/IP module updates for Linux and Windows' (#387) from mueller/tcpip-updates into development

Reviewed-on: fsfw/fsfw#387
This commit is contained in:
Steffen Gaisser 2021-03-16 15:19:40 +01:00
commit b757c5523c
24 changed files with 813 additions and 495 deletions

View File

@ -32,3 +32,5 @@ else()
endif() endif()
endif() endif()
add_subdirectory(common)

View File

@ -0,0 +1,3 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
tcpipCommon.cpp
)

View File

@ -0,0 +1,36 @@
#include "tcpipCommon.h"
void tcpip::determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std::string &protStr,
std::string &srcString) {
if(protocol == Protocol::TCP) {
protStr = "TCP";
}
else if(protocol == Protocol::UDP) {
protStr = "UDP";
}
else {
protStr = "Unknown protocol";
}
if(errorSrc == ErrorSources::SETSOCKOPT_CALL) {
srcString = "setsockopt call";
}
else if(errorSrc == ErrorSources::SOCKET_CALL) {
srcString = "socket call";
}
else if(errorSrc == ErrorSources::LISTEN_CALL) {
srcString = "listen call";
}
else if(errorSrc == ErrorSources::ACCEPT_CALL) {
srcString = "accept call";
}
else if(errorSrc == ErrorSources::RECVFROM_CALL) {
srcString = "recvfrom call";
}
else if(errorSrc == ErrorSources::GETADDRINFO_CALL) {
srcString = "getaddrinfo call";
}
else {
srcString = "unknown call";
}
}

36
osal/common/tcpipCommon.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef FSFW_OSAL_COMMON_TCPIPCOMMON_H_
#define FSFW_OSAL_COMMON_TCPIPCOMMON_H_
#include "../../timemanager/clockDefinitions.h"
#include <string>
namespace tcpip {
const char* const DEFAULT_UDP_SERVER_PORT = "7301";
const char* const DEFAULT_TCP_SERVER_PORT = "7303";
enum class Protocol {
UDP,
TCP
};
enum class ErrorSources {
GETADDRINFO_CALL,
SOCKET_CALL,
SETSOCKOPT_CALL,
BIND_CALL,
RECV_CALL,
RECVFROM_CALL,
LISTEN_CALL,
ACCEPT_CALL,
SENDTO_CALL
};
void determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std::string& protStr,
std::string& srcString);
}
#endif /* FSFW_OSAL_COMMON_TCPIPCOMMON_H_ */

View File

@ -24,5 +24,7 @@ MutexIF* MutexFactory::createMutex() {
} }
void MutexFactory::deleteMutex(MutexIF* mutex) { void MutexFactory::deleteMutex(MutexIF* mutex) {
if(mutex != nullptr) {
delete mutex; delete mutex;
} }
}

View File

@ -16,6 +16,7 @@ target_sources(${LIB_FSFW_NAME}
TcUnixUdpPollingTask.cpp TcUnixUdpPollingTask.cpp
TmTcUnixUdpBridge.cpp TmTcUnixUdpBridge.cpp
Timer.cpp Timer.cpp
tcpipHelpers.cpp
) )
find_package(Threads REQUIRED) find_package(Threads REQUIRED)

View File

@ -1,7 +1,9 @@
#include "TcUnixUdpPollingTask.h" #include "TcUnixUdpPollingTask.h"
#include "tcpipHelpers.h"
#include "../../globalfunctions/arrayprinter.h" #include "../../globalfunctions/arrayprinter.h"
#include <errno.h> #define FSFW_UDP_RCV_WIRETAPPING_ENABLED 0
TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId, TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId,
object_id_t tmtcUnixUdpBridge, size_t frameSize, object_id_t tmtcUnixUdpBridge, size_t frameSize,
@ -15,8 +17,8 @@ TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId,
this->frameSize = DEFAULT_MAX_FRAME_SIZE; this->frameSize = DEFAULT_MAX_FRAME_SIZE;
} }
// Set up reception buffer with specified frame size. /* Set up reception buffer with specified frame size.
// For now, it is assumed that only one frame is held in the buffer! For now, it is assumed that only one frame is held in the buffer! */
receptionBuffer.reserve(this->frameSize); receptionBuffer.reserve(this->frameSize);
receptionBuffer.resize(this->frameSize); receptionBuffer.resize(this->frameSize);
@ -31,34 +33,37 @@ TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId,
TcUnixUdpPollingTask::~TcUnixUdpPollingTask() {} TcUnixUdpPollingTask::~TcUnixUdpPollingTask() {}
ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) { ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) {
// Poll for new UDP datagrams in permanent loop. /* Sender Address is cached here. */
while(1) {
//! Sender Address is cached here.
struct sockaddr_in senderAddress; struct sockaddr_in senderAddress;
socklen_t senderSockLen = sizeof(senderAddress); socklen_t senderAddressSize = sizeof(senderAddress);
ssize_t bytesReceived = recvfrom(serverUdpSocket,
receptionBuffer.data(), frameSize, receptionFlags,
reinterpret_cast<sockaddr*>(&senderAddress), &senderSockLen);
if(bytesReceived < 0) {
// handle error
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcSocketPollingTask::performOperation: Reception"
"error." << std::endl;
#endif
handleReadError();
/* Poll for new UDP datagrams in permanent loop. */
while(true) {
ssize_t bytesReceived = recvfrom(
serverUdpSocket,
receptionBuffer.data(),
frameSize,
receptionFlags,
reinterpret_cast<sockaddr*>(&senderAddress),
&senderAddressSize
);
if(bytesReceived < 0) {
/* Handle error */
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcSocketPollingTask::performOperation: Reception error." << std::endl;
#endif
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 500);
continue; continue;
} }
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
// sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived
// << " bytes received" << std::endl; << " bytes received" << std::endl;
#endif #endif
ReturnValue_t result = handleSuccessfullTcRead(bytesReceived); ReturnValue_t result = handleSuccessfullTcRead(bytesReceived);
if(result != HasReturnvaluesIF::RETURN_FAILED) { if(result != HasReturnvaluesIF::RETURN_FAILED) {
} }
tmtcBridge->registerCommConnect();
tmtcBridge->checkAndSetClientAddress(senderAddress); tmtcBridge->checkAndSetClientAddress(senderAddress);
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
@ -67,15 +72,21 @@ ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) {
ReturnValue_t TcUnixUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) { ReturnValue_t TcUnixUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
store_address_t storeId; store_address_t storeId;
ReturnValue_t result = tcStore->addData(&storeId,
receptionBuffer.data(), bytesRead); #if FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
// arrayprinter::print(receptionBuffer.data(), bytesRead); arrayprinter::print(receptionBuffer.data(), bytesRead);
#endif
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data " sif::error << "TcUnixUdpPollingTask::handleSuccessfullTcRead: Data "
"storage failed" << std::endl; "storage failed" << std::endl;
sif::error << "Packet size: " << bytesRead << std::endl; sif::error << "Packet size: " << bytesRead << std::endl;
#endif #else
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
@ -83,10 +94,13 @@ ReturnValue_t TcUnixUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message); result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Serial Polling: Sending message to queue failed" sif::error << "TcUnixUdpPollingTask::handleSuccessfullTcRead: Sending message to queue "
<< std::endl; "failed" << std::endl;
#endif #else
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
tcStore->deleteData(storeId); tcStore->deleteData(storeId);
} }
return result; return result;
@ -111,15 +125,16 @@ ReturnValue_t TcUnixUdpPollingTask::initialize() {
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
serverUdpSocket = tmtcBridge->serverSocket;
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
ReturnValue_t TcUnixUdpPollingTask::initializeAfterTaskCreation() { ReturnValue_t TcUnixUdpPollingTask::initializeAfterTaskCreation() {
// Initialize the destination after task creation. This ensures /* Initialize the destination after task creation. This ensures
// that the destination will be set in the TMTC bridge. that the destination has already been set in the TMTC bridge. */
targetTcDestination = tmtcBridge->getRequestQueue(); targetTcDestination = tmtcBridge->getRequestQueue();
/* The server socket is set up in the bridge intialization. Calling this function here
ensures that it is set up properly in any case*/
serverUdpSocket = tmtcBridge->serverSocket;
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
@ -135,24 +150,3 @@ void TcUnixUdpPollingTask::setTimeout(double timeoutSeconds) {
#endif #endif
} }
} }
// TODO: sleep after error detection to prevent spam
void TcUnixUdpPollingTask::handleReadError() {
switch(errno) {
case(EAGAIN): {
// todo: When working in timeout mode, this will occur more often
// and is not an error.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcUnixUdpPollingTask::handleReadError: Timeout."
<< std::endl;
#endif
break;
}
default: {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcUnixUdpPollingTask::handleReadError: "
<< strerror(errno) << std::endl;
#endif
}
}
}

View File

@ -48,6 +48,7 @@ private:
object_id_t tmtcBridgeId = objects::NO_OBJECT; object_id_t tmtcBridgeId = objects::NO_OBJECT;
TmTcUnixUdpBridge* tmtcBridge = nullptr; TmTcUnixUdpBridge* tmtcBridge = nullptr;
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE; MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
//! Reception flags: https://linux.die.net/man/2/recvfrom. //! Reception flags: https://linux.die.net/man/2/recvfrom.
int receptionFlags = 0; int receptionFlags = 0;
@ -61,7 +62,6 @@ private:
timeval receptionTimeout; timeval receptionTimeout;
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead); ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
void handleReadError();
}; };
#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */ #endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */

View File

@ -1,203 +1,166 @@
#include "TmTcUnixUdpBridge.h" #include "TmTcUnixUdpBridge.h"
#include "tcpipHelpers.h"
#include "../../serviceinterface/ServiceInterface.h" #include "../../serviceinterface/ServiceInterface.h"
#include "../../ipc/MutexGuard.h" #include "../../ipc/MutexGuard.h"
#include <errno.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <cstring>
TmTcUnixUdpBridge::TmTcUnixUdpBridge(object_id_t objectId, //! Debugging preprocessor define.
object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId, #define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0
uint16_t serverPort, uint16_t clientPort):
const std::string TmTcUnixUdpBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_UDP_SERVER_PORT;
TmTcUnixUdpBridge::TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort):
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
if(udpServerPort == "") {
this->udpServerPort = DEFAULT_UDP_SERVER_PORT;
}
else {
this->udpServerPort = udpServerPort;
}
mutex = MutexFactory::instance()->createMutex(); mutex = MutexFactory::instance()->createMutex();
communicationLinkUp = false;
uint16_t setServerPort = DEFAULT_UDP_SERVER_PORT;
if(serverPort != 0xFFFF) {
setServerPort = serverPort;
} }
uint16_t setClientPort = DEFAULT_UDP_CLIENT_PORT; ReturnValue_t TmTcUnixUdpBridge::initialize() {
if(clientPort != 0xFFFF) { using namespace tcpip;
setClientPort = clientPort;
}
// Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html ReturnValue_t result = TmTcBridge::initialize();
//clientSocket = socket(AF_INET, SOCK_DGRAM, 0); if(result != HasReturnvaluesIF::RETURN_OK) {
serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(serverSocket < 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open" sif::error << "TmTcUnixUdpBridge::initialize: TmTcBridge initialization failed!"
" UDP socket!" << std::endl;
#endif
handleSocketError();
return;
}
serverAddress.sin_family = AF_INET;
// Accept packets from any interface.
//serverAddress.sin_addr.s_addr = inet_addr("127.73.73.0");
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(setServerPort);
serverAddressLen = sizeof(serverAddress);
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &serverSocketOptions,
sizeof(serverSocketOptions));
clientAddress.sin_family = AF_INET;
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
clientAddress.sin_port = htons(setClientPort);
clientAddressLen = sizeof(clientAddress);
int result = bind(serverSocket,
reinterpret_cast<struct sockaddr*>(&serverAddress),
serverAddressLen);
if(result == -1) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not bind "
"local port " << setServerPort << " to server socket!"
<< std::endl; << std::endl;
#endif #endif
handleBindError(); return result;
return;
} }
struct addrinfo *addrResult = nullptr;
struct addrinfo hints;
std::memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
/* Set up UDP socket:
https://man7.org/linux/man-pages/man3/getaddrinfo.3.html
Passing nullptr as the first parameter and specifying AI_PASSIVE in hints will cause
getaddrinfo to assign the address 0.0.0.0 (any address) */
int retval = getaddrinfo(nullptr, udpServerPort.c_str(), &hints, &addrResult);
if (retval != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Retrieving address info failed!" <<
std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}
/* Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html */
serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol);
if(serverSocket < 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open UDP socket!" <<
std::endl;
#else
sif::printError("TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open UDP socket!\n");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
freeaddrinfo(addrResult);
handleError(Protocol::UDP, ErrorSources::SOCKET_CALL);
return HasReturnvaluesIF::RETURN_FAILED;
}
retval = bind(serverSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
if(retval != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind "
"local port (" << udpServerPort << ") to server socket!" << std::endl;
#endif
freeaddrinfo(addrResult);
handleError(Protocol::UDP, ErrorSources::BIND_CALL);
return HasReturnvaluesIF::RETURN_FAILED;
}
return HasReturnvaluesIF::RETURN_OK;
} }
TmTcUnixUdpBridge::~TmTcUnixUdpBridge() { TmTcUnixUdpBridge::~TmTcUnixUdpBridge() {
if(mutex != nullptr) {
MutexFactory::instance()->deleteMutex(mutex);
}
close(serverSocket);
} }
ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
int flags = 0; int flags = 0;
MutexGuard lock(mutex, MutexIF::TimeoutType::WAITING, 10); /* The target address can be set by different threads so this lock ensures thread-safety */
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
if(ipAddrAnySet){ if(ipAddrAnySet){
clientAddress.sin_addr.s_addr = htons(INADDR_ANY); clientAddress.sin_addr.s_addr = htons(INADDR_ANY);
//clientAddress.sin_addr.s_addr = inet_addr("127.73.73.1"); clientAddressLen = sizeof(clientAddress);
clientAddressLen = sizeof(serverAddress);
} }
// char ipAddress [15]; #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 char ipAddress [15];
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, sif::debug << "IP Address Sender: "<<
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; inet_ntop(AF_INET,&clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
#endif #endif
ssize_t bytesSent = sendto(serverSocket, data, dataLen, flags, ssize_t bytesSent = sendto(
reinterpret_cast<sockaddr*>(&clientAddress), clientAddressLen); serverSocket,
data,
dataLen,
flags,
reinterpret_cast<sockaddr*>(&clientAddress),
clientAddressLen
);
if(bytesSent < 0) { if(bytesSent < 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixUdpBridge::sendTm: Send operation failed." sif::warning << "TmTcUnixUdpBridge::sendTm: Send operation failed." << std::endl;
<< std::endl;
#endif #endif
handleSendError(); tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SENDTO_CALL);
} }
#if FSFW_CPP_OSTREAM_ENABLED == 1
// sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
// " sent." << std::endl; sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
" sent." << std::endl;
#endif #endif
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
void TmTcUnixUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) { void TmTcUnixUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) {
MutexGuard lock(mutex, MutexIF::TimeoutType::WAITING, 10); /* The target address can be set by different threads so this lock ensures thread-safety */
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
// char ipAddress [15]; #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 char ipAddress [15];
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET, sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
// &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
// sif::debug << "IP Address Old: " << inet_ntop(AF_INET, sif::debug << "IP Address Old: " << inet_ntop(AF_INET,
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
#endif #endif
registerCommConnect();
// Set new IP address if it has changed. /* Set new IP address if it has changed. */
if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) { if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) {
clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr; clientAddress = newAddress;
clientAddressLen = sizeof(clientAddress); clientAddressLen = sizeof(clientAddress);
} }
} }
void TmTcUnixUdpBridge::setMutexProperties(MutexIF::TimeoutType timeoutType,
void TmTcUnixUdpBridge::handleSocketError() { dur_millis_t timeoutMs) {
// See: https://man7.org/linux/man-pages/man2/socket.2.html this->timeoutType = timeoutType;
switch(errno) { this->mutexTimeoutMs = timeoutMs;
case(EACCES):
case(EINVAL):
case(EMFILE):
case(ENFILE):
case(EAFNOSUPPORT):
case(ENOBUFS):
case(ENOMEM):
case(EPROTONOSUPPORT):
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixBridge::handleSocketError: Socket creation failed"
<< " with " << strerror(errno) << std::endl;
#endif
break;
default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixBridge::handleSocketError: Unknown error"
<< std::endl;
#endif
break;
}
}
void TmTcUnixUdpBridge::handleBindError() {
// See: https://man7.org/linux/man-pages/man2/bind.2.html
switch(errno) {
case(EACCES): {
/*
Ephermeral ports can be shown with following command:
sysctl -A | grep ip_local_port_range
*/
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixBridge::handleBindError: Port access issue."
"Ports 1-1024 are reserved on UNIX systems and require root "
"rights while ephermeral ports should not be used as well."
<< std::endl;
#endif
}
break;
case(EADDRINUSE):
case(EBADF):
case(EINVAL):
case(ENOTSOCK):
case(EADDRNOTAVAIL):
case(EFAULT):
case(ELOOP):
case(ENAMETOOLONG):
case(ENOENT):
case(ENOMEM):
case(ENOTDIR):
case(EROFS): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixBridge::handleBindError: Socket creation failed"
<< " with " << strerror(errno) << std::endl;
#endif
break;
}
default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixBridge::handleBindError: Unknown error"
<< std::endl;
#endif
break;
}
}
void TmTcUnixUdpBridge::handleSendError() {
switch(errno) {
default: {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixBridge::handleSendError: "
<< strerror(errno) << std::endl;
#else
sif::printError("TmTcUnixBridge::handleSendError: %s\n",
strerror(errno));
#endif
}
}
} }
void TmTcUnixUdpBridge::setClientAddressToAny(bool ipAddrAnySet){ void TmTcUnixUdpBridge::setClientAddressToAny(bool ipAddrAnySet){

View File

@ -7,45 +7,47 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/udp.h> #include <netinet/udp.h>
class TmTcUnixUdpBridge: public TmTcBridge { class TmTcUnixUdpBridge:
public TmTcBridge {
friend class TcUnixUdpPollingTask; friend class TcUnixUdpPollingTask;
public: public:
// The ports chosen here should not be used by any other process.
// List of used ports on Linux: /etc/services /* The ports chosen here should not be used by any other process.
static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301; List of used ports on Linux: /etc/services */
static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302; static const std::string DEFAULT_UDP_SERVER_PORT;
TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination, TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId, object_id_t tmStoreId, object_id_t tcStoreId,
uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF); std::string serverPort = "");
virtual~ TmTcUnixUdpBridge(); virtual~ TmTcUnixUdpBridge();
/**
* Set properties of internal mutex.
*/
void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs);
ReturnValue_t initialize() override;
void checkAndSetClientAddress(sockaddr_in& clientAddress); void checkAndSetClientAddress(sockaddr_in& clientAddress);
void setClientAddressToAny(bool ipAddrAnySet); void setClientAddressToAny(bool ipAddrAnySet);
protected: protected:
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
private: private:
int serverSocket = 0; int serverSocket = 0;
std::string udpServerPort;
const int serverSocketOptions = 0;
struct sockaddr_in clientAddress; struct sockaddr_in clientAddress;
socklen_t clientAddressLen = 0; socklen_t clientAddressLen = 0;
struct sockaddr_in serverAddress;
socklen_t serverAddressLen = 0;
bool ipAddrAnySet = false; bool ipAddrAnySet = false;
//! Access to the client address is mutex protected as it is set //! Access to the client address is mutex protected as it is set by another task.
//! by another task. MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
dur_millis_t mutexTimeoutMs = 20;
MutexIF* mutex; MutexIF* mutex;
void handleSocketError();
void handleBindError();
void handleSendError();
}; };
#endif /* FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_ */ #endif /* FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_ */

108
osal/linux/tcpipHelpers.cpp Normal file
View File

@ -0,0 +1,108 @@
#include "tcpipHelpers.h"
#include "../../tasks/TaskFactory.h"
#include <errno.h>
#include <string>
void tcpip::handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration) {
int errCode = errno;
std::string protocolString;
std::string errorSrcString;
determineErrorStrings(protocol, errorSrc, protocolString, errorSrcString);
std::string infoString;
switch(errCode) {
case(EACCES): {
infoString = "EACCES";
break;
}
case(EINVAL): {
infoString = "EINVAL";
break;
}
case(EAGAIN): {
infoString = "EAGAIN";
break;
}
case(EMFILE): {
infoString = "EMFILE";
break;
}
case(ENFILE): {
infoString = "ENFILE";
break;
}
case(EAFNOSUPPORT): {
infoString = "EAFNOSUPPORT";
break;
}
case(ENOBUFS): {
infoString = "ENOBUFS";
break;
}
case(ENOMEM): {
infoString = "ENOMEM";
break;
}
case(EPROTONOSUPPORT): {
infoString = "EPROTONOSUPPORT";
break;
}
case(EADDRINUSE): {
infoString = "EADDRINUSE";
break;
}
case(EBADF): {
infoString = "EBADF";
break;
}
case(ENOTSOCK): {
infoString = "ENOTSOCK";
break;
}
case(EADDRNOTAVAIL): {
infoString = "EADDRNOTAVAIL";
break;
}
case(EFAULT): {
infoString = "EFAULT";
break;
}
case(ELOOP): {
infoString = "ELOOP";
break;
}
case(ENAMETOOLONG): {
infoString = "ENAMETOOLONG";
break;
}
case(ENOENT): {
infoString = "ENOENT";
break;
}
case(ENOTDIR): {
infoString = "ENOTDIR";
break;
}
case(EROFS): {
infoString = "EROFS";
break;
}
default: {
infoString = "Error code: " + std::to_string(errCode);
}
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "tcpip::handleError: " << protocolString << " | " << errorSrcString <<
" | " << infoString << std::endl;
#else
sif::printWarning("tcpip::handleError: %s | %s | %s\n", protocolString,
errorSrcString, infoString);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
if(sleepDuration > 0) {
TaskFactory::instance()->delayTask(sleepDuration);
}
}

14
osal/linux/tcpipHelpers.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef FSFW_OSAL_LINUX_TCPIPHELPERS_H_
#define FSFW_OSAL_LINUX_TCPIPHELPERS_H_
#include "../../timemanager/clockDefinitions.h"
#include "../common/tcpipCommon.h"
namespace tcpip {
void handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration = 0);
}
#endif /* FSFW_OSAL_LINUX_TCPIPHELPERS_H_ */

View File

@ -1,6 +1,8 @@
target_sources(${LIB_FSFW_NAME} PRIVATE target_sources(${LIB_FSFW_NAME} PRIVATE
TcWinUdpPollingTask.cpp TcWinUdpPollingTask.cpp
TmTcWinUdpBridge.cpp TmTcWinUdpBridge.cpp
TcWinTcpServer.cpp
tcpipHelpers.cpp
) )
target_link_libraries(${LIB_FSFW_NAME} PRIVATE target_link_libraries(${LIB_FSFW_NAME} PRIVATE

View File

@ -0,0 +1,129 @@
#include "TcWinTcpServer.h"
#include "tcpipHelpers.h"
#include "../../serviceinterface/ServiceInterface.h"
#include <winsock2.h>
#include <ws2tcpip.h>
const std::string TcWinTcpServer::DEFAULT_TCP_SERVER_PORT = "7301";
const std::string TcWinTcpServer::DEFAULT_TCP_CLIENT_PORT = "7302";
TcWinTcpServer::TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge,
std::string customTcpServerPort):
SystemObject(objectId), tcpPort(customTcpServerPort) {
if(tcpPort == "") {
tcpPort = DEFAULT_TCP_SERVER_PORT;
}
}
ReturnValue_t TcWinTcpServer::initialize() {
using namespace tcpip;
int retval = 0;
struct addrinfo *addrResult = nullptr;
struct addrinfo hints;
/* Initiates Winsock DLL. */
WSAData wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable Winsock DLL. */
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " <<
err << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}
ZeroMemory(&hints, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult);
if (retval != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TcWinTcpServer::TcWinTcpServer: Retrieving address info failed!" <<
std::endl;
#endif
handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL);
return HasReturnvaluesIF::RETURN_FAILED;
}
/* Open TCP (stream) socket */
listenerTcpSocket = socket(addrResult->ai_family, addrResult->ai_socktype,
addrResult->ai_protocol);
if(listenerTcpSocket == INVALID_SOCKET) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TcWinTcpServer::TcWinTcpServer: Socket creation failed!" << std::endl;
#endif
freeaddrinfo(addrResult);
handleError(Protocol::TCP, ErrorSources::SOCKET_CALL);
return HasReturnvaluesIF::RETURN_FAILED;
}
retval = bind(listenerTcpSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
if(retval == SOCKET_ERROR) {
sif::warning << "TcWinTcpServer::TcWinTcpServer: Binding socket failed!" <<
std::endl;
freeaddrinfo(addrResult);
handleError(Protocol::TCP, ErrorSources::BIND_CALL);
}
freeaddrinfo(addrResult);
return HasReturnvaluesIF::RETURN_OK;
}
TcWinTcpServer::~TcWinTcpServer() {
closesocket(listenerTcpSocket);
WSACleanup();
}
ReturnValue_t TcWinTcpServer::performOperation(uint8_t opCode) {
using namespace tcpip;
/* If a connection is accepted, the corresponding socket will be assigned to the new socket */
SOCKET clientSocket;
sockaddr_in clientSockAddr;
int connectorSockAddrLen = 0;
int retval = 0;
/* Listen for connection requests permanently for lifetime of program */
while(true) {
retval = listen(listenerTcpSocket, currentBacklog);
if(retval == SOCKET_ERROR) {
handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500);
continue;
}
clientSocket = accept(listenerTcpSocket, reinterpret_cast<sockaddr*>(&clientSockAddr),
&connectorSockAddrLen);
if(clientSocket == INVALID_SOCKET) {
handleError(Protocol::TCP, ErrorSources::ACCEPT_CALL, 500);
continue;
};
retval = recv(clientSocket, reinterpret_cast<char*>(receptionBuffer.data()),
receptionBuffer.size(), 0);
if(retval > 0) {
#if FSFW_TCP_RCV_WIRETAPPING_ENABLED == 1
sif::info << "TcWinTcpServer::performOperation: Received " << retval << " bytes."
std::endl;
#endif
handleError(Protocol::TCP, ErrorSources::RECV_CALL, 500);
}
else if(retval == 0) {
}
else {
}
/* Done, shut down connection */
retval = shutdown(clientSocket, SD_SEND);
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -0,0 +1,47 @@
#ifndef FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_
#define FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_
#include "../../objectmanager/SystemObject.h"
#include "../../tasks/ExecutableObjectIF.h"
#include <string>
#include <vector>
//! Debugging preprocessor define.
#define FSFW_TCP_RCV_WIRETAPPING_ENABLED 0
/**
* @brief Windows TCP server used to receive telecommands on a Windows Host
* @details
* Based on: https://docs.microsoft.com/en-us/windows/win32/winsock/complete-server-code
*/
class TcWinTcpServer:
public SystemObject,
public ExecutableObjectIF {
public:
/* The ports chosen here should not be used by any other process. */
static const std::string DEFAULT_TCP_SERVER_PORT;
static const std::string DEFAULT_TCP_CLIENT_PORT;
TcWinTcpServer(object_id_t objectId, object_id_t tmtcUnixUdpBridge,
std::string customTcpServerPort = "");
virtual~ TcWinTcpServer();
ReturnValue_t initialize() override;
ReturnValue_t performOperation(uint8_t opCode) override;
private:
std::string tcpPort;
SOCKET listenerTcpSocket = 0;
struct sockaddr_in tcpAddress;
int tcpAddrLen = sizeof(tcpAddress);
int currentBacklog = 3;
std::vector<uint8_t> receptionBuffer;
int tcpSockOpt = 0;
};
#endif /* FSFW_OSAL_WINDOWS_TCWINTCPSERVER_H_ */

View File

@ -1,4 +1,5 @@
#include "TcWinUdpPollingTask.h" #include "TcWinUdpPollingTask.h"
#include "tcpipHelpers.h"
#include "../../globalfunctions/arrayprinter.h" #include "../../globalfunctions/arrayprinter.h"
#include "../../serviceinterface/ServiceInterfaceStream.h" #include "../../serviceinterface/ServiceInterfaceStream.h"
@ -31,25 +32,29 @@ TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId,
TcWinUdpPollingTask::~TcWinUdpPollingTask() {} TcWinUdpPollingTask::~TcWinUdpPollingTask() {}
ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) { ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) {
/* Poll for new UDP datagrams in permanent loop. */ /* Sender Address is cached here. */
while(true) {
//! Sender Address is cached here.
struct sockaddr_in senderAddress; struct sockaddr_in senderAddress;
int senderAddressSize = sizeof(senderAddress); int senderAddressSize = sizeof(senderAddress);
int bytesReceived = recvfrom(serverUdpSocket,
reinterpret_cast<char*>(receptionBuffer.data()), frameSize, /* Poll for new UDP datagrams in permanent loop. */
receptionFlags, reinterpret_cast<sockaddr*>(&senderAddress), while(true) {
&senderAddressSize); int bytesReceived = recvfrom(
serverUdpSocket,
reinterpret_cast<char*>(receptionBuffer.data()),
frameSize,
receptionFlags,
reinterpret_cast<sockaddr*>(&senderAddress),
&senderAddressSize
);
if(bytesReceived == SOCKET_ERROR) { if(bytesReceived == SOCKET_ERROR) {
/* Handle error */ /* Handle error */
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcWinUdpPollingTask::performOperation: Reception" sif::error << "TcWinUdpPollingTask::performOperation: Reception error." << std::endl;
" error." << std::endl;
#endif #endif
handleReadError(); tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 1000);
continue; continue;
} }
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_WIRETAPPING_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived << sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived <<
" bytes received" << std::endl; " bytes received" << std::endl;
#endif #endif
@ -58,7 +63,6 @@ ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) {
if(result != HasReturnvaluesIF::RETURN_FAILED) { if(result != HasReturnvaluesIF::RETURN_FAILED) {
} }
tmtcBridge->registerCommConnect();
tmtcBridge->checkAndSetClientAddress(senderAddress); tmtcBridge->checkAndSetClientAddress(senderAddress);
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
@ -67,17 +71,20 @@ ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) {
ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) { ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
store_address_t storeId; store_address_t storeId;
ReturnValue_t result = tcStore->addData(&storeId,
receptionBuffer.data(), bytesRead); #if FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1
#if FSFW_UDP_WIRETAPPING_ENABLED == 1 arrayprinter::print(receptionBuffer.data(), bytesRead);
arrayprinter::print(receptionBuffer.data(), bytesRead);#
#endif #endif
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning<< "TcSerialPollingTask::transferPusToSoftwareBus: Data " sif::warning<< "TcWinUdpPollingTask::transferPusToSoftwareBus: Data storage failed." <<
"storage failed" << std::endl; std::endl;
sif::warning << "Packet size: " << bytesRead << std::endl; sif::warning << "Packet size: " << bytesRead << std::endl;
#endif #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
@ -85,9 +92,12 @@ ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message); result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "Serial Polling: Sending message to queue failed" << std::endl; sif::warning << "TcWinUdpPollingTask::handleSuccessfullTcRead: "
#endif " Sending message to queue failed" << std::endl;
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
tcStore->deleteData(storeId); tcStore->deleteData(storeId);
} }
return result; return result;
@ -97,8 +107,7 @@ ReturnValue_t TcWinUdpPollingTask::initialize() {
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE); tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
if (tcStore == nullptr) { if (tcStore == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcSerialPollingTask::initialize: TC Store uninitialized!" sif::error << "TcWinUdpPollingTask::initialize: TC store uninitialized!" << std::endl;
<< std::endl;
#endif #endif
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
@ -106,25 +115,21 @@ ReturnValue_t TcWinUdpPollingTask::initialize() {
tmtcBridge = objectManager->get<TmTcWinUdpBridge>(tmtcBridgeId); tmtcBridge = objectManager->get<TmTcWinUdpBridge>(tmtcBridgeId);
if(tmtcBridge == nullptr) { if(tmtcBridge == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Invalid" sif::error << "TcWinUdpPollingTask::initialize: Invalid TMTC bridge object!" <<
" TMTC bridge object!" << std::endl; std::endl;
#endif #endif
return ObjectManagerIF::CHILD_INIT_FAILED; return ObjectManagerIF::CHILD_INIT_FAILED;
} }
serverUdpSocket = tmtcBridge->serverSocket;
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_WIRETAPPING_ENABLED == 1
sif::info << "TcWinUdpPollingTask::initialize: Server UDP socket " << serverUdpSocket <<
std::endl;
#endif
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
ReturnValue_t TcWinUdpPollingTask::initializeAfterTaskCreation() { ReturnValue_t TcWinUdpPollingTask::initializeAfterTaskCreation() {
// Initialize the destination after task creation. This ensures /* Initialize the destination after task creation. This ensures
// that the destination has already been set in the TMTC bridge. that the destination has already been set in the TMTC bridge. */
targetTcDestination = tmtcBridge->getRequestQueue(); targetTcDestination = tmtcBridge->getRequestQueue();
/* The server socket is set up in the bridge intialization. Calling this function here
ensures that it is set up properly in any case*/
serverUdpSocket = tmtcBridge->serverSocket;
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
@ -139,39 +144,3 @@ void TcWinUdpPollingTask::setTimeout(double timeoutSeconds) {
#endif #endif
} }
} }
void TcWinUdpPollingTask::handleReadError() {
int error = WSAGetLastError();
switch(error) {
case(WSANOTINITIALISED): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TcWinUdpPollingTask::handleReadError: WSANOTINITIALISED: "
<< "WSAStartup(...) call " << "necessary" << std::endl;
#endif
break;
}
case(WSAEFAULT): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TcWinUdpPollingTask::handleReadError: WSADEFAULT: "
<< "Bad address " << std::endl;
#endif
break;
}
case(WSAEINVAL): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TcWinUdpPollingTask::handleReadError: WSAEINVAL: "
<< "Invalid input parameters. " << std::endl;
#endif
break;
}
default: {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TcWinUdpPollingTask::handleReadError: Error code: "
<< error << std::endl;
#endif
break;
}
}
// to prevent spam.
Sleep(1000);
}

View File

@ -51,10 +51,12 @@ private:
object_id_t tmtcBridgeId = objects::NO_OBJECT; object_id_t tmtcBridgeId = objects::NO_OBJECT;
TmTcWinUdpBridge* tmtcBridge = nullptr; TmTcWinUdpBridge* tmtcBridge = nullptr;
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE; MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
//! Reception flags: https://linux.die.net/man/2/recvfrom.
//! See: https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recvfrom
int receptionFlags = 0; int receptionFlags = 0;
//! Server socket, which is member of TMTC bridge and is assigned in constructor //! Server socket, which is member of TMTC bridge.
//! Will be cached shortly after SW intialization.
SOCKET serverUdpSocket = 0; SOCKET serverUdpSocket = 0;
std::vector<uint8_t> receptionBuffer; std::vector<uint8_t> receptionBuffer;
@ -63,7 +65,6 @@ private:
timeval receptionTimeout; timeval receptionTimeout;
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead); ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
void handleReadError();
}; };
#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */ #endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */

View File

@ -1,18 +1,38 @@
#include "TmTcWinUdpBridge.h" #include "TmTcWinUdpBridge.h"
#include "tcpipHelpers.h"
#include <fsfw/serviceinterface/ServiceInterface.h>
#include <fsfw/ipc/MutexGuard.h> #include <fsfw/ipc/MutexGuard.h>
#include <ws2tcpip.h>
#if defined(_MSC_VER) //! Debugging preprocessor define.
#include <BaseTsd.h> #define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0
typedef SSIZE_T ssize_t;
#endif
TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, const std::string TmTcWinUdpBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_UDP_SERVER_PORT;
object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId,
uint16_t serverPort, uint16_t clientPort): TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort):
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
if(udpServerPort == "") {
this->udpServerPort = DEFAULT_UDP_SERVER_PORT;
}
else {
this->udpServerPort = udpServerPort;
}
mutex = MutexFactory::instance()->createMutex(); mutex = MutexFactory::instance()->createMutex();
communicationLinkUp = false; communicationLinkUp = false;
}
ReturnValue_t TmTcWinUdpBridge::initialize() {
ReturnValue_t result = TmTcBridge::initialize();
if(result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcWinUdpBridge::initialize: TmTcBridge initialization failed!"
<< std::endl;
#endif
return result;
}
/* Initiates Winsock DLL. */ /* Initiates Winsock DLL. */
WSAData wsaData; WSAData wsaData;
@ -22,71 +42,65 @@ TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId,
/* Tell the user that we could not find a usable */ /* Tell the user that we could not find a usable */
/* Winsock DLL. */ /* Winsock DLL. */
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge:" sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " <<
"WSAStartup failed with error: " << err << std::endl; err << std::endl;
#else
sif::printError("TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: %d\n",
err);
#endif #endif
return; return HasReturnvaluesIF::RETURN_FAILED;
} }
uint16_t setServerPort = DEFAULT_UDP_SERVER_PORT; struct addrinfo *addrResult = nullptr;
if(serverPort != 0xFFFF) { struct addrinfo hints;
setServerPort = serverPort;
}
uint16_t setClientPort = DEFAULT_UDP_CLIENT_PORT; ZeroMemory(&hints, sizeof (hints));
if(clientPort != 0xFFFF) { hints.ai_family = AF_INET;
setClientPort = clientPort; hints.ai_socktype = SOCK_DGRAM;
} hints.ai_protocol = IPPROTO_UDP;
hints.ai_flags = AI_PASSIVE;
/* Set up UDP socket: /* Set up UDP socket:
https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket https://en.wikipedia.org/wiki/Getaddrinfo
*/ Passing nullptr as the first parameter and specifying AI_PASSIVE in hints will cause
serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); getaddrinfo to assign the address 0.0.0.0 (any address) */
int retval = getaddrinfo(nullptr, udpServerPort.c_str(), &hints, &addrResult);
if (retval != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Retrieving address info failed!" <<
std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED;
}
serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol);
if(serverSocket == INVALID_SOCKET) { if(serverSocket == INVALID_SOCKET) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open UDP socket!" << sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open UDP socket!" <<
std::endl; std::endl;
#endif #endif
handleSocketError(); freeaddrinfo(addrResult);
return; tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SOCKET_CALL);
return HasReturnvaluesIF::RETURN_FAILED;
} }
serverAddress.sin_family = AF_INET; retval = bind(serverSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
if(retval != 0) {
/* Accept packets from any interface. (potentially insecure). */
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(setServerPort);
serverAddressLen = sizeof(serverAddress);
int result = setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&serverSocketOptions),
sizeof(serverSocketOptions));
if(result != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not set socket options!" <<
std::endl;
#endif
handleSocketError();
}
clientAddress.sin_family = AF_INET;
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
clientAddress.sin_port = htons(setClientPort);
clientAddressLen = sizeof(clientAddress);
result = bind(serverSocket,
reinterpret_cast<struct sockaddr*>(&serverAddress),
serverAddressLen);
if(result != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind " sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind "
"local port " << setServerPort << " to server socket!" "local port (" << udpServerPort << ") to server socket!" << std::endl;
<< std::endl;
#endif #endif
handleBindError(); freeaddrinfo(addrResult);
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::BIND_CALL);
} }
freeaddrinfo(addrResult);
return HasReturnvaluesIF::RETURN_OK;
} }
TmTcWinUdpBridge::~TmTcWinUdpBridge() { TmTcWinUdpBridge::~TmTcWinUdpBridge() {
if(mutex != nullptr) {
MutexFactory::instance()->deleteMutex(mutex);
}
closesocket(serverSocket); closesocket(serverSocket);
WSACleanup(); WSACleanup();
} }
@ -94,6 +108,9 @@ TmTcWinUdpBridge::~TmTcWinUdpBridge() {
ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) { ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
int flags = 0; int flags = 0;
/* The target address can be set by different threads so this lock ensures thread-safety */
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
clientAddress.sin_addr.s_addr = htons(INADDR_ANY); clientAddress.sin_addr.s_addr = htons(INADDR_ANY);
clientAddressLen = sizeof(serverAddress); clientAddressLen = sizeof(serverAddress);
@ -102,15 +119,19 @@ ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
&clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl; &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
#endif #endif
ssize_t bytesSent = sendto(serverSocket, int bytesSent = sendto(
reinterpret_cast<const char*>(data), dataLen, flags, serverSocket,
reinterpret_cast<sockaddr*>(&clientAddress), clientAddressLen); reinterpret_cast<const char*>(data),
dataLen,
flags,
reinterpret_cast<sockaddr*>(&clientAddress),
clientAddressLen
);
if(bytesSent == SOCKET_ERROR) { if(bytesSent == SOCKET_ERROR) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcWinUdpBridge::sendTm: Send operation failed." sif::warning << "TmTcWinUdpBridge::sendTm: Send operation failed." << std::endl;
<< std::endl;
#endif #endif
handleSendError(); tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SENDTO_CALL);
} }
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were" sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
@ -119,8 +140,9 @@ ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) { void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) {
MutexGuard lock(mutex, MutexIF::TimeoutType::WAITING, 10); /* The target address can be set by different threads so this lock ensures thread-safety */
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
char ipAddress [15]; char ipAddress [15];
@ -133,93 +155,13 @@ void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) {
/* Set new IP address if it has changed. */ /* Set new IP address if it has changed. */
if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) { if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) {
clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr; clientAddress = newAddress;
clientAddressLen = sizeof(clientAddress); clientAddressLen = sizeof(clientAddress);
} }
} }
void TmTcWinUdpBridge::handleSocketError() { void TmTcWinUdpBridge::setMutexProperties(MutexIF::TimeoutType timeoutType,
int errCode = WSAGetLastError(); dur_millis_t timeoutMs) {
switch(errCode) { this->timeoutType = timeoutType;
case(WSANOTINITIALISED): { this->mutexTimeoutMs = timeoutMs;
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::handleSocketError: WSANOTINITIALISED: WSAStartup"
" call necessary" << std::endl;
#endif
break;
} }
default: {
/*
https://docs.microsoft.com/en-us/windows/win32/winsock/
windows-sockets-error-codes-2
*/
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::handleSocketError: Error code: " << errCode << std::endl;
#endif
break;
}
}
}
void TmTcWinUdpBridge::handleBindError() {
int errCode = WSAGetLastError();
switch(errCode) {
case(WSANOTINITIALISED): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TmTcWinUdpBridge::handleBindError: WSANOTINITIALISED: "
<< "WSAStartup call necessary" << std::endl;
#endif
break;
}
case(WSAEADDRINUSE): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "TmTcWinUdpBridge::handleBindError: WSAEADDRINUSE: "
"Port is already in use!" << std::endl;
#endif
break;
}
default: {
/*
https://docs.microsoft.com/en-us/windows/win32/winsock/
windows-sockets-error-codes-2
*/
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TmTcWinUdpBridge::handleBindError: Error code: "
<< errCode << std::endl;
#endif
break;
}
}
}
void TmTcWinUdpBridge::handleSendError() {
int errCode = WSAGetLastError();
switch(errCode) {
case(WSANOTINITIALISED): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TmTcWinUdpBridge::handleSendError: WSANOTINITIALISED: "
<< "WSAStartup(...) call necessary" << std::endl;
#endif
break;
}
case(WSAEADDRNOTAVAIL): {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TmTcWinUdpBridge::handleSendError: WSAEADDRNOTAVAIL: "
<< "Check target address. " << std::endl;
#endif
break;
}
default: {
/*
https://docs.microsoft.com/en-us/windows/win32/winsock/
windows-sockets-error-codes-2
*/
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "TmTcWinUdpBridge::handleSendError: Error code: "
<< errCode << std::endl;
#endif
break;
}
}
}

View File

@ -4,54 +4,41 @@
#include "../../tmtcservices/TmTcBridge.h" #include "../../tmtcservices/TmTcBridge.h"
#include <winsock2.h> #include <winsock2.h>
#include <windows.h>
//! Debugging preprocessor define.
#define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0
class TmTcWinUdpBridge: public TmTcBridge { class TmTcWinUdpBridge: public TmTcBridge {
friend class TcWinUdpPollingTask; friend class TcWinUdpPollingTask;
public: public:
/* The ports chosen here should not be used by any other process. */ /* The ports chosen here should not be used by any other process. */
static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301; static const std::string DEFAULT_UDP_SERVER_PORT;
static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302;
TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination, TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId, object_id_t tmStoreId, object_id_t tcStoreId, std::string udpServerPort = "");
uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF);
virtual~ TmTcWinUdpBridge(); virtual~ TmTcWinUdpBridge();
void checkAndSetClientAddress(sockaddr_in clientAddress); /**
* Set properties of internal mutex.
*/
void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs);
ReturnValue_t initialize() override;
void checkAndSetClientAddress(sockaddr_in& clientAddress);
protected: protected:
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
private: private:
SOCKET serverSocket = 0; SOCKET serverSocket = 0;
std::string udpServerPort;
const int serverSocketOptions = 0;
struct sockaddr_in clientAddress; struct sockaddr_in clientAddress;
int clientAddressLen = 0; int clientAddressLen = 0;
struct sockaddr_in serverAddress; //! Access to the client address is mutex protected as it is set by another task.
int serverAddressLen = 0; MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
dur_millis_t mutexTimeoutMs = 20;
//! Access to the client address is mutex protected as it is set
//! by another task.
MutexIF* mutex; MutexIF* mutex;
enum class ErrorSources {
SOCKET_CALL,
SETSOCKOPT_CALL
}; };
void handleSocketError();
void handleBindError();
void handleSendError();
};
#endif /* FSFW_OSAL_HOST_TMTCWINUDPBRIDGE_H_ */ #endif /* FSFW_OSAL_HOST_TMTCWINUDPBRIDGE_H_ */

View File

@ -0,0 +1,63 @@
#include "tcpipHelpers.h"
#include <FSFWConfig.h>
#include "../../tasks/TaskFactory.h"
#include "../../serviceinterface/ServiceInterface.h"
#include <winsock2.h>
#include <string>
void tcpip::handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration) {
#if FSFW_VERBOSE_LEVEL >= 1
int errCode = WSAGetLastError();
std::string protocolString;
std::string errorSrcString;
determineErrorStrings(protocol, errorSrc, protocolString, errorSrcString);
std::string infoString;
switch(errCode) {
case(WSANOTINITIALISED): {
infoString = "WSANOTINITIALISED";
break;
}
case(WSAEADDRINUSE): {
infoString = "WSAEADDRINUSE";
break;
}
case(WSAEFAULT): {
infoString = "WSAEFAULT";
break;
}
case(WSAEADDRNOTAVAIL): {
infoString = "WSAEADDRNOTAVAIL";
break;
}
case(WSAEINVAL): {
infoString = "WSAEINVAL";
break;
}
default: {
/*
https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
*/
infoString = "Error code: " + std::to_string(errCode);
break;
}
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "tcpip::handleError: " << protocolString << " | " << errorSrcString <<
" | " << infoString << std::endl;
#else
sif::printWarning("tcpip::handleError: %s | %s | %s\n", protocolString,
errorSrcString, infoString);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
if(sleepDuration > 0) {
TaskFactory::instance()->delayTask(sleepDuration);
}
}

View File

@ -0,0 +1,15 @@
#ifndef FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_
#define FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_
#include "../../timemanager/clockDefinitions.h"
#include "../common/tcpipCommon.h"
namespace tcpip {
void handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration = 0);
}
#endif /* FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_ */

View File

@ -173,6 +173,9 @@ ReturnValue_t TmTcBridge::handleTmQueue() {
ReturnValue_t TmTcBridge::storeDownlinkData(TmTcMessage *message) { ReturnValue_t TmTcBridge::storeDownlinkData(TmTcMessage *message) {
store_address_t storeId = 0; store_address_t storeId = 0;
if(tmFifo == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
if(tmFifo->full()) { if(tmFifo->full()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1

View File

@ -275,7 +275,6 @@ TEST_CASE("DataSetTest" , "[DataSetTest]") {
} }
sharedSet.setReadCommitProtectionBehaviour(true); sharedSet.setReadCommitProtectionBehaviour(true);
} }
/* we need to reset the subscription list because the pool owner /* we need to reset the subscription list because the pool owner

View File

@ -206,7 +206,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1)); CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1));
} }
SECTION("VariableUpdateTest") { SECTION("VariableNotificationTest") {
/* Acquire subscription interface */ /* Acquire subscription interface */
ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface(); ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface();
REQUIRE(subscriptionIF != nullptr); REQUIRE(subscriptionIF != nullptr);