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 #include <cerrno>
19 #include <cstring>
20 #include <new>
21 
22 namespace Drv {
23 
24 UdpSocket::UdpSocket() : IpSocket(), m_recv_configured(false) {
25  (void)::memset(&m_addr_send, 0, sizeof(m_addr_send));
26  (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
27 }
28 
29 UdpSocket::~UdpSocket() = default;
30 
31 SocketIpStatus UdpSocket::configure(const char* const ipv4_address,
32  const U16 port,
33  const U32 timeout_seconds,
34  const U32 timeout_microseconds) {
35  (void)ipv4_address;
36  (void)port;
37  (void)timeout_seconds;
38  (void)timeout_microseconds;
39  FW_ASSERT(false); // Must use configureSend and/or configureRecv
41 }
42 
43 SocketIpStatus UdpSocket::configureSend(const char* const ipv4_address,
44  const U16 port,
45  const U32 timeout_seconds,
46  const U32 timeout_microseconds) {
47  FW_ASSERT(ipv4_address != nullptr);
48  FW_ASSERT(this->isValidPort(port));
49  FW_ASSERT(timeout_microseconds < 1000000);
50  return IpSocket::configure(ipv4_address, port, timeout_seconds, timeout_microseconds);
51 }
52 
53 SocketIpStatus UdpSocket::configureRecv(const char* const ipv4_address, const U16 port) {
54  FW_ASSERT(ipv4_address != nullptr);
55  FW_ASSERT(this->isValidPort(port));
56  FW_ASSERT(Fw::StringUtils::string_length(ipv4_address, static_cast<FwSizeType>(SOCKET_MAX_IPV4_ADDRESS_SIZE)) <
57  static_cast<FwSizeType>(SOCKET_MAX_IPV4_ADDRESS_SIZE));
58 
59  // Initialize the receive address structure
60  (void)::memset(&m_addr_recv, 0, sizeof(m_addr_recv));
61  m_addr_recv.sin_family = AF_INET;
62  m_addr_recv.sin_port = htons(port);
63 
64  // Convert IPv4 address (dotted-quad) to network-order in_addr
65  SocketIpStatus status = IpSocket::addressToIp4(ipv4_address, &m_addr_recv.sin_addr);
66  if (status != SOCK_SUCCESS) {
67  return status;
68  }
69 
70  this->m_recv_configured = true;
71  return SOCK_SUCCESS;
72 }
73 
75  return ntohs(this->m_addr_recv.sin_port);
76 }
77 
79  FW_ASSERT(fd != -1);
80  struct sockaddr_in address = this->m_addr_recv;
81 
82  // OS specific settings
83 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
84  address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
85 #endif
86  // UDP (for receiving) requires bind to an address to the socket
87  if (::bind(fd, reinterpret_cast<struct sockaddr*>(&address), sizeof(address)) < 0) {
88  return SOCK_FAILED_TO_BIND;
89  }
90 
91  socklen_t size = sizeof(address);
92  const int socknameStatus = ::getsockname(fd, reinterpret_cast<struct sockaddr*>(&address), &size);
93  if (socknameStatus == -1) {
95  }
96 
97  // Update m_addr_recv with the actual port assigned (for ephemeral port support)
98  this->m_addr_recv.sin_port = address.sin_port;
99 
100  return SOCK_SUCCESS;
101 }
102 
104  if (this->m_port == 0 && !this->m_recv_configured) {
105  return SOCK_INVALID_CALL; // Neither send nor receive is configured
106  }
107 
108  SocketIpStatus status = SOCK_SUCCESS;
109 
110  // Initialize address structure to zero before use
111  struct sockaddr_in address;
112  (void)::memset(&address, 0, sizeof(address));
113 
114  U16 port = this->m_port;
115  U16 recv_port = ntohs(this->m_addr_recv.sin_port);
116 
117  // Acquire a socket, or return error
118  int socketFd = ::socket(AF_INET, SOCK_DGRAM, 0);
119  if (socketFd == -1) {
121  }
122 
123  // Apply the configured timeouts. This socket may send even when no send port was
124  // configured, because a send port of 0 means "reply to the source of the last
125  // datagram received", so the timeout must not be tied to `port != 0` below.
126  status = this->setupTimeouts(socketFd);
127  if (status != SOCK_SUCCESS) {
128  (void)::close(socketFd);
129  return status;
130  }
131 
132  // May not be sending in all cases
133  if (port != 0) {
134  // Set up the address port and name
135  address.sin_family = AF_INET;
136  address.sin_port = htons(this->m_port);
137 
138  // OS specific settings
139 #if defined TGT_OS_TYPE_VXWORKS || TGT_OS_TYPE_DARWIN
140  address.sin_len = static_cast<U8>(sizeof(struct sockaddr_in));
141 #endif
142 
143  // Convert the configured IPv4 address (dotted-quad) to a network-order in_addr.
144  status = IpSocket::addressToIp4(this->m_ipv4_address, &(address.sin_addr));
145  if (status != SOCK_SUCCESS) {
146  Fw::Logger::log("Failed to parse IPv4 address %s: %d\n", this->m_ipv4_address, static_cast<I32>(status));
147  (void)::close(socketFd);
148  return status;
149  };
150 
151  if (IpSocket::setupSocketOptions(socketFd) != SOCK_SUCCESS) {
152  (void)::close(socketFd);
154  }
155 
156  static_assert(sizeof(m_addr_send) == sizeof(address), "Send address must match the local address structure");
157  (void)memcpy(&this->m_addr_send, &address, sizeof(this->m_addr_send));
158  }
159 
160  // Only bind if configureRecv was called (including ephemeral)
161  if (this->m_recv_configured) {
162  status = this->bind(socketFd);
163 
164  if (status != SOCK_SUCCESS) {
165  (void)::close(socketFd); // Closing FD as a retry will reopen send side
166  return status;
167  }
168  }
169 
170  // Log message for UDP
171  char recv_addr[INET_ADDRSTRLEN];
172  const char* recv_addr_str = inet_ntop(AF_INET, &(this->m_addr_recv.sin_addr), recv_addr, INET_ADDRSTRLEN);
173  if (recv_addr_str == nullptr) {
174  (void)Fw::StringUtils::string_copy(recv_addr, "INVALID_ADDR", INET_ADDRSTRLEN);
175  }
176 
177  if ((port == 0) && (recv_port > 0)) {
178  Fw::Logger::log("Setup to only receive udp at %s:%hu\n", recv_addr, recv_port);
179  } else if ((port > 0) && (recv_port == 0)) {
180  Fw::Logger::log("Setup to only send udp at %s:%hu\n", this->m_ipv4_address, port);
181  } else if ((port > 0) && (recv_port > 0)) {
182  Fw::Logger::log("Setup to receive udp at %s:%hu and send to %s:%hu\n", recv_addr, recv_port,
183  this->m_ipv4_address, port);
184  }
185 
186  FW_ASSERT(status == SOCK_SUCCESS, static_cast<FwAssertArgType>(status));
187  socketDescriptor.fd = socketFd;
188  return status;
189 }
190 
192  const U8* const data,
193  const FwSizeType size) {
194  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
195  FW_ASSERT((size == 0) || (data != nullptr)); // Data pointer should not be null for nonzero sizes
196 
197  // In respond-to-sender mode the destination is learned from the first received
198  // packet; a send before then has nowhere to go and is a send error, not a bug
199  if (this->m_addr_send.sin_family == 0) {
200  errno = ENOTCONN;
201  return -1;
202  }
203 
204  return static_cast<FwSignedSizeType>(
205  ::sendto(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_SEND_FLAGS,
206  reinterpret_cast<struct sockaddr*>(&this->m_addr_send), sizeof(this->m_addr_send)));
207 }
208 
210  U8* const data,
211  const FwSizeType size) {
212  FW_ASSERT(this->m_addr_recv.sin_family != 0); // Make sure the address was previously setup
213  FW_ASSERT(socketDescriptor.fd >= 0); // File descriptor should be valid
214  FW_ASSERT(data != nullptr); // Data pointer should not be null
215 
216  // Initialize sender address structure to zero
217  struct sockaddr_in sender_addr;
218  (void)::memset(&sender_addr, 0, sizeof(sender_addr));
219 
220  socklen_t sender_addr_len = sizeof(sender_addr);
221  FwSignedSizeType received = static_cast<FwSignedSizeType>(
222  ::recvfrom(socketDescriptor.fd, data, static_cast<size_t>(size), SOCKET_IP_RECV_FLAGS,
223  reinterpret_cast<struct sockaddr*>(&sender_addr), &sender_addr_len));
224  // If we have not configured a send port, set it to the source of the last received packet
225  if (received >= 0 && this->m_addr_send.sin_port == 0) {
226  this->m_addr_send = sender_addr;
227  this->m_port = ntohs(sender_addr.sin_port);
228  Fw::Logger::log("Configured send port to %hu as specified by the last received packet.\n", this->m_port);
229  }
230  return received;
231 }
232 
233 SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const FwSizeType size) {
234  // Note: socketDescriptor.fd can be -1 in some test cases
235  FW_ASSERT((size == 0) || (data != nullptr));
236 
237  // Special case for zero-length datagrams in UDP
238  if (size == 0) {
239  errno = 0;
240  FwSignedSizeType sent = this->sendProtocol(socketDescriptor, data, 0);
241  if (sent == -1) {
242  if (errno == EINTR) {
243  // For zero-length datagrams, we'll just try once more if interrupted
244  errno = 0;
245  sent = this->sendProtocol(socketDescriptor, data, 0);
246  }
247 
248  if (sent == -1) {
249  if ((errno == EBADF) || (errno == ECONNRESET)) {
250  return SOCK_DISCONNECTED;
251  } else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
253  } else {
254  return SOCK_SEND_ERROR;
255  }
256  }
257  }
258  // For zero-length datagrams in UDP, success is either 0 or a non-negative value
259  return SOCK_SUCCESS;
260  }
261 
262  // For non-zero-length data, delegate to the base class implementation
263  return IpSocket::send(socketDescriptor, data, size);
264 }
265 
267  // For UDP, a return of 0 from recvfrom means a 0-byte datagram was received.
268  // This is a success case for UDP, not a disconnection.
269  return SOCK_SUCCESS;
270 }
271 
272 } // namespace Drv
Failed to send after configured retries.
Definition: IpSocket.hpp:42
U16 getRecvPort()
get the port being received on
Definition: UdpSocket.cpp:74
Socket disconnected during read or send.
Definition: IpSocket.hpp:38
Socket open failed.
Definition: IpSocket.hpp:31
Interrupted status for retries.
Definition: IpSocket.hpp:36
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:233
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:54
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:30
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:191
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:83
SocketIpStatus configureRecv(const char *const ipv4_address, const U16 port)
configure the udp socket for incoming transmissions
Definition: UdpSocket.cpp:53
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:138
virtual ~UdpSocket()
to cleanup state created at instantiation
U16 m_port
IP address port used.
Definition: IpSocket.hpp:273
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:31
Socket operation successful.
Definition: IpSocket.hpp:30
UdpSocket()
Constructor for client socket udp implementation.
Definition: UdpSocket.cpp:24
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:43
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:78
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:209
SocketIpStatus setupSocketOptions(int socketFd)
setup the socket options of the input socket as defined in IpCfg.hpp
Definition: IpSocket.cpp:232
Failed to read back port from connection.
Definition: IpSocket.hpp:44
virtual bool isValidPort(U16 port) const
Check if the given port is valid for the socket.
Definition: IpSocket.cpp:50
uint8_t U8
8-bit unsigned integer
Definition: BasicTypes.h:54
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:274
void close(const SocketDescriptor &socketDescriptor)
closes the socket
Definition: IpSocket.cpp:111
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:266
SocketIpStatus openProtocol(SocketDescriptor &socketDescriptor) override
udp specific implementation for opening a socket.
Definition: UdpSocket.cpp:103
FwSizeType string_length(const CHAR *source, FwSizeType buffer_size)
get the length of the source string
Definition: StringUtils.cpp:32