F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
UdpSocket.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title UdpSocket.cpp
3 // \author mstarch
4 // \brief cpp file for UdpSocket core implementation classes
5 //
6 // \copyright
7 // Copyright 2009-2020, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12 #include <Drv/Ip/UdpSocket.hpp>
13 #include <Fw/FPrimeBasicTypes.hpp>
14 #include <Fw/Logger/Logger.hpp>
15 #include <Fw/Types/Assert.hpp>
16 #include <Fw/Types/StringUtils.hpp>
17 
18 #ifdef TGT_OS_TYPE_VXWORKS
19 #include <errnoLib.h>
20 #include <fioLib.h>
21 #include <hostLib.h>
22 #include <inetLib.h>
23 #include <ioLib.h>
24 #include <sockLib.h>
25 #include <socket.h>
26 #include <sysLib.h>
27 #include <taskLib.h>
28 #include <vxWorks.h>
29 #include <cstring>
30 #else
31 #include <arpa/inet.h>
32 #include <sys/socket.h>
33 #include <unistd.h>
34 #endif
35 
36 #include <cerrno>
37 #include <cstring>
38 #include <new>
39 
40 namespace Drv {
41 
42 UdpSocket::UdpSocket() : IpSocket(), m_recv_configured(false) {
43  (void)::memset(&m_addr_send, 0, sizeof(m_addr_send));
44  (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
45 }
46 
47 UdpSocket::~UdpSocket() = default;
48 
49 SocketIpStatus UdpSocket::configure(const char* const ipv4_address,
50  const U16 port,
51  const U32 timeout_seconds,
52  const U32 timeout_microseconds) {
53  (void)ipv4_address;
54  (void)port;
55  (void)timeout_seconds;
56  (void)timeout_microseconds;
57  FW_ASSERT(0); // Must use configureSend and/or configureRecv
59 }
60 
61 SocketIpStatus UdpSocket::configureSend(const char* const ipv4_address,
62  const U16 port,
63  const U32 timeout_seconds,
64  const U32 timeout_microseconds) {
65  FW_ASSERT(ipv4_address != nullptr);
66  FW_ASSERT(this->isValidPort(port));
67  FW_ASSERT(timeout_microseconds < 1000000);
68  return IpSocket::configure(ipv4_address, port, timeout_seconds, timeout_microseconds);
69 }
70 
71 SocketIpStatus UdpSocket::configureRecv(const char* const ipv4_address, const U16 port) {
72  FW_ASSERT(ipv4_address != nullptr);
73  FW_ASSERT(this->isValidPort(port));
74  FW_ASSERT(Fw::StringUtils::string_length(ipv4_address, static_cast<FwSizeType>(SOCKET_MAX_IPV4_ADDRESS_SIZE)) <
75  static_cast<FwSizeType>(SOCKET_MAX_IPV4_ADDRESS_SIZE));
76 
77  // Initialize the receive address structure
78  (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
79  m_addr_recv.sin_family = AF_INET;
80  m_addr_recv.sin_port = htons(port);
81 
82  // Convert IPv4 address (dotted-quad) to network-order in_addr
83  SocketIpStatus status = IpSocket::addressToIp4(ipv4_address, &m_addr_recv.sin_addr);
84  if (status != SOCK_SUCCESS) {
85  return status;
86  }
87 
88  this->m_recv_configured = true;
89  return SOCK_SUCCESS;
90 }
91 
93  return ntohs(this->m_addr_recv.sin_port);
94 }
95 
97  FW_ASSERT(fd != -1);
98  struct sockaddr_in address = this->m_addr_recv;
99 
100  // OS specific settings
101 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
102  address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
103 #endif
104  // UDP (for receiving) requires bind to an address to the socket
105  if (::bind(fd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
106  return SOCK_FAILED_TO_BIND;
107  }
108 
109  socklen_t size = sizeof(address);
110  if (::getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &size) == -1) {
112  }
113 
114  // Update m_addr_recv with the actual port assigned (for ephemeral port support)
115  this->m_addr_recv.sin_port = address.sin_port;
116 
117  return SOCK_SUCCESS;
118 }
119 
121  if (this->m_port == 0 && !this->m_recv_configured) {
122  return SOCK_INVALID_CALL; // Neither send nor receive is configured
123  }
124 
125  SocketIpStatus status = SOCK_SUCCESS;
126  int socketFd = -1;
127 
128  // Initialize address structure to zero before use
129  struct sockaddr_in address;
130  (void)::memset(&address, 0, sizeof(address));
131 
132  U16 port = this->m_port;
133  U16 recv_port = ntohs(this->m_addr_recv.sin_port);
134 
135  // Acquire a socket, or return error
136  if ((socketFd = ::socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
138  }
139 
140  // May not be sending in all cases
141  if (port != 0) {
142  // Set up the address port and name
143  address.sin_family = AF_INET;
144  address.sin_port = htons(this->m_port);
145 
146  // OS specific settings
147 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
148  address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
149 #endif
150 
151  // Convert the configured IPv4 address (dotted-quad) to a network-order in_addr.
152  status = IpSocket::addressToIp4(this->m_ipv4_address, &(address.sin_addr));
153  if (status != SOCK_SUCCESS) {
154  Fw::Logger::log("Failed to parse IPv4 address %s: %d\n", this->m_ipv4_address, static_cast<I32>(status));
155  ::close(socketFd);
156  return status;
157  };
158 
159  if (IpSocket::setupSocketOptions(socketFd) != SOCK_SUCCESS) {
160  ::close(socketFd);
162  }
163 
164  // Now apply timeouts
165  status = this->setupTimeouts(socketFd);
166  if (status != SOCK_SUCCESS) {
167  ::close(socketFd);
168  return status;
169  }
170  FW_ASSERT(sizeof(this->m_addr_send) == sizeof(address), static_cast<FwAssertArgType>(sizeof(this->m_addr_send)),
171  static_cast<FwAssertArgType>(sizeof(address)));
172  (void)memcpy(&this->m_addr_send, &address, sizeof(this->m_addr_send));
173  }
174 
175  // Only bind if configureRecv was called (including ephemeral)
176  if (this->m_recv_configured) {
177  status = this->bind(socketFd);
178 
179  if (status != SOCK_SUCCESS) {
180  (void)::close(socketFd); // Closing FD as a retry will reopen send side
181  return status;
182  }
183  }
184 
185  // Log message for UDP
186  char recv_addr[INET_ADDRSTRLEN];
187  const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
188  if (recv_addr_str == nullptr) {
189  (void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
190  }
191 
192  if ((port == 0) && (recv_port > 0)) {
193  Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
194  } else if ((port > 0) && (recv_port == 0)) {
195  Fw::Logger::log("Setup to only send udp at %s:%hu\n", this->m_ipv4_address, port);
196  } else if ((port > 0) && (recv_port > 0)) {
197  Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port,
198  this->m_ipv4_address, port);
199  }
200 
201  FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
202  socketDescriptor.fd = socketFd;
203  return status;
204 }
205 
207  const U8* const data,
208  const FwSizeType size) {
209  FW_ASSERT(this->m_addr_send.sin_family != 0); // Make sure the address was previously setup
210  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
211  FW_ASSERT(data != nullptr); // Data pointer should not be null
212 
213  return static_cast<FwSignedSizeType>(
214  ::sendto(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS,
215  reinterpret_cast<struct sockaddr*>(&this->m_addr_send), sizeof(this->m_addr_send)));
216 }
217 
219  U8* const data,
220  const FwSizeType size) {
221  FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
222  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
223  FW_ASSERT(data != nullptr); // Data pointer should not be null
224 
225  // Initialize sender address structure to zero
226  struct sockaddr_in sender_addr;
227  (void)::memset(&sender_addr, 0, sizeof(sender_addr));
228 
229  socklen_t sender_addr_len = sizeof(sender_addr);
230  FwSignedSizeType received = static_cast<FwSignedSizeType>(
231  ::recvfrom(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_RECV_FLAGS,
232  reinterpret_cast<struct sockaddr*>(&sender_addr), &sender_addr_len));
233  // If we have not configured a send port, set it to the source of the last received packet
234  if (received >= 0 && this->m_addr_send.sin_port == 0) {
235  this->m_addr_send = sender_addr;
236  this->m_port = ntohs(sender_addr.sin_port);
237  Fw::Logger::log("Configured send port to %hu as specified by the last received packet.\n", this->m_port);
238  }
239  return received;
240 }
241 
242 SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) {
243  // Note: socketDescriptor.fd can be -1 in some test cases
244  FW_ASSERT((size == 0) || (data != nullptr));
245 
246  // Special case for zero-length datagrams in UDP
247  if (size == 0) {
248  errno = 0;
249  FwSignedSizeType sent = this->sendProtocol(socketDescriptor, data, 0);
250  if (sent == -1) {
251  if (errno == EINTR) {
252  // For zero-length datagrams, we'll just try once more if interrupted
253  errno = 0;
254  sent = this->sendProtocol(socketDescriptor, data, 0);
255  }
256 
257  if (sent == -1) {
258  if ((errno == EBADF) || (errno == ECONNRESET)) {
259  return SOCK_DISCONNECTED;
260  } else {
261  return SOCK_SEND_ERROR;
262  }
263  }
264  }
265  // For zero-length datagrams in UDP, success is either 0 or a non-negative value
266  return SOCK_SUCCESS;
267  }
268 
269  // For non-zero-length data, delegate to the base class implementation
270  return IpSocket::send(socketDescriptor, data, size);
271 }
272 
274  // For UDP, a return of 0 from recvfrom means a 0-byte datagram was received.
275  // This is a success case for UDP, not a disconnection.
276  return SOCK_SUCCESS;
277 }
278 
279 } // namespace Drv
Failed to send after configured retries.
Definition: IpSocket.hpp:42
U16 getRecvPort()
get the port being received on
Definition: UdpSocket.cpp:92
virtual bool isValidPort(U16 port)
Check if the given port is valid for the socket.
Definition: IpSocket.cpp:71
Failed to read socket with disconnect.
Definition: IpSocket.hpp:38
Socket open failed.
Definition: IpSocket.hpp:31
SocketIpStatus send(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size) override
UDP-specific implementation of send that handles zero-length datagrams correctly. ...
Definition: UdpSocket.cpp:242
Failed to configure socket.
Definition: IpSocket.hpp:35
PlatformSizeType FwSizeType
SocketIpStatus setupTimeouts(int socketFd)
setup the socket timeout properties of the opened outgoing socket
Definition: IpSocket.cpp:75
virtual SocketIpStatus configure(const char *const ipv4_address, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds)
configure the ip socket with an IPv4 address and transmission timeouts
Definition: IpSocket.cpp:51
FwSignedSizeType sendProtocol(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size) override
Protocol specific implementation of send. Called directly with retry from send.
Definition: UdpSocket.cpp:206
static void log(const char *format,...)
log a formated string with supplied arguments
Definition: Logger.cpp:21
static SocketIpStatus addressToIp4(const char *const ipv4_address, void *const out)
converts a given IPv4 address in dotted-quad form "x.x.x.x" to a network-order in_addr structure...
Definition: IpSocket.cpp:92
SocketIpStatus configureRecv(const char *const ipv4_address, const U16 port)
configure the udp socket for incoming transmissions
Definition: UdpSocket.cpp:71
int fd
Used for all sockets to track the communication file descriptor.
Definition: IpSocket.hpp:22
PlatformSignedSizeType FwSignedSizeType
virtual SocketIpStatus send(const SocketDescriptor &socketDescriptor, const U8 *const data, const FwSizeType size)
send data out the IP socket from the given buffer
Definition: IpSocket.cpp:144
virtual ~UdpSocket()
to cleanup state created at instantiation
U16 m_port
IP address port used.
Definition: IpSocket.hpp:250
SocketIpStatus configure(const char *const ipv4_address, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds) override
configure is disabled
Definition: UdpSocket.cpp:49
Socket operation successful.
Definition: IpSocket.hpp:30
UdpSocket()
Constructor for client socket udp implementation.
Definition: UdpSocket.cpp:42
SocketIpStatus configureSend(const char *const ipv4_address, const U16 port, const U32 send_timeout_seconds, const U32 send_timeout_microseconds)
configure the udp socket for outgoing transmissions
Definition: UdpSocket.cpp:61
SocketIpStatus bind(const int fd)
bind the UDP to a port such that it can receive packets at the previously configured port ...
Definition: UdpSocket.cpp:96
char * string_copy(char *destination, const char *source, FwSizeType num)
copy string with null-termination guaranteed
Definition: StringUtils.cpp:7
FwSignedSizeType recvProtocol(const SocketDescriptor &socketDescriptor, U8 *const data, const FwSizeType size) override
Protocol specific implementation of recv. Called directly with error handling from recv...
Definition: UdpSocket.cpp:218
SocketIpStatus setupSocketOptions(int socketFd)
setup the socket options of the input socket as defined in IpCfg.hpp
Definition: IpSocket.cpp:229
Failed to read back port from connection.
Definition: IpSocket.hpp:44
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:53
Operation is invalid.
Definition: IpSocket.hpp:48
char m_ipv4_address[SOCKET_MAX_IPV4_ADDRESS_SIZE]
IPv4 address (dotted-quad "x.x.x.x")
Definition: IpSocket.hpp:251
void close(const SocketDescriptor &socketDescriptor)
closes the socket
Definition: IpSocket.cpp:119
SocketIpStatus
Status enumeration for socket return values.
Definition: IpSocket.hpp:29
Helper base-class for setting up Berkeley sockets.
Definition: IpSocket.hpp:57
Failed to bind to socket.
Definition: IpSocket.hpp:39
#define FW_ASSERT(...)
Definition: Assert.hpp:14
SocketIpStatus handleZeroReturn() override
Handle zero return from recvProtocol for UDP.
Definition: UdpSocket.cpp:273
SocketIpStatus openProtocol(SocketDescriptor &socketDescriptor) override
udp specific implementation for opening a socket.
Definition: UdpSocket.cpp:120
FwSizeType string_length(const CHAR *source, FwSizeType buffer_size)
get the length of the source string
Definition: StringUtils.cpp:32