Skip to content

Conversation

@vincewoo
Copy link
Collaborator

@vincewoo vincewoo commented Jun 12, 2025

Related Issue(s) #3716
Has Unit Tests (y/n) y
Documentation Included (y/n) n

Change Description

This change cleans up Drv::Ip::UdpSocket to remove the members m_state, m_recv_port, and m_recv_hostname in favour of two new members m_addr_send and m_addr_recv. As part of this change we are also removing the dependency on dynamic memory for storing state.

This change also improved error handling, edge case handling, and memory safety.

Rationale

Simplifies code and fixes bug of having duplicate data structures storing the same state.

Testing/Review Recommendations

Existing UTs cover the changes made except for handling 0-length datagrams. A new UT was added specifically to test the behaviour of 0-length datagrams.

Future Work

None

@LeStarch LeStarch self-requested a review June 12, 2025 17:44
Copy link
Collaborator

@LeStarch LeStarch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot more here than I expected. Also, it seems that many asserts have been changed to if-checks. The general rule is:

  1. If it is clearly a coding error, assert. (e.g. a char* was passed in as nullptr, or bind was called before open)
  2. Use an if to check for things that could be a error with the run.

#ifdef TGT_OS_TYPE_VXWORKS
#include <socket.h>
#include <inetLib.h>
#elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should just have the else clause and VXWORKS. Most patforms will put the headers as UNIX does.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move the #include for linux/darwin into else?

@vincewoo vincewoo requested a review from LeStarch June 14, 2025 04:15
@thomas-bc thomas-bc changed the title Refactoring Drv::Ip::UdpSocket to clean up state members and remove dependency on dynamic memory Refactor Drv::Ip::UdpSocket to clean up state members and remove dependency on dynamic memory Jun 16, 2025
Copy link
Collaborator

@LeStarch LeStarch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few more cleanup items.

#ifdef TGT_OS_TYPE_VXWORKS
#include <socket.h>
#include <inetLib.h>
#elif defined TGT_OS_TYPE_LINUX || TGT_OS_TYPE_DARWIN
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move the #include for linux/darwin into else?

@vincewoo vincewoo requested a review from LeStarch June 18, 2025 14:34
@vincewoo
Copy link
Collaborator Author

@LeStarch The latest check-ins include a fix for a latent bug in the UdpSocket implementation for the handling of 0-length datagrams that you pointed out. Due to UDP supporting 0-length data and TCP not technically supporting 0-length data it required a little bit more refactoring to properly detangle the send/recv implementation in IpSocket and allow UdpSocket to override these commands.

ASSERT_EQ(receiver.configureRecv("127.0.0.1", port), Drv::SOCK_SUCCESS);
ASSERT_EQ(receiver.open(recv_fd), Drv::SOCK_SUCCESS);

// Set a custom, longer receive timeout for this specific test to 500ms
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious as to why....


if (status == Drv::SOCK_SUCCESS) {
received_size += bytes_actually_received;
} else if (status == Drv::SOCK_NO_DATA_AVAILABLE) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point of this method is to wait (even if no data is available) until such a time as the whole message received (in that the message might span multiple timeouts).

Thus I am confused why this is being commuted to a failure.

return SOCK_NO_DATA_AVAILABLE;
} else if (errno == EINTR) {
// Interrupted system call. Retry if not the last iteration.
if (i == (SOCKET_MAX_ITERATIONS - 1)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doe we need both this and the check at the end? Can't we just continue again?

}

SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
// Note: socketDescriptor.fd can be -1 in some test cases
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? How could this ever be -1?

return received;
}

SocketIpStatus UdpSocket::send(const SocketDescriptor& socketDescriptor, const U8* const data, const U32 size) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a special send? Can we not just update the send method on the IpSocket?

It seems odd that:

  1. We fixed asserts in the send in IpSocket to allow size==0, but also override the method
  2. recv is updated to respect 0 length, in Ip, but send is done in UDP.

// Nothing to be received
if ((size == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
// Loop primarily for EINTR. Other conditions should lead to an earlier exit.
for (U32 i = 0; i < SOCKET_MAX_ITERATIONS; i++) {

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always true because i <= 0.
SocketIpStatus IpSocket::handleZeroReturn() {
// For TCP (which IpSocket primarily serves as a base for, or when not overridden),
// a return of 0 from ::recv means the peer has performed an orderly shutdown.
return SOCK_DISCONNECTED;

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter req_read has not been checked.
}

// For non-zero-length data, delegate to the base class implementation
return IpSocket::send(socketDescriptor, data, size);

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter socketDescriptor has not been checked.
}

// For non-zero-length data, delegate to the base class implementation
return IpSocket::send(socketDescriptor, data, size);

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter data has not been checked.
@vincewoo vincewoo requested a review from LeStarch June 19, 2025 01:45
Copy link
Collaborator

@LeStarch LeStarch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to understand this comment

//TODO: Uncomment FW_ASSERT for socketDescriptor.fd once we fix TcpClientTester to not pass in uninitialized socketDescriptor

@LeStarch LeStarch merged commit 3894ddc into nasa:devel Jun 26, 2025
50 checks passed
@vincewoo vincewoo deleted the FP-3716 branch August 6, 2025 06:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants