-
Notifications
You must be signed in to change notification settings - Fork 3k
Handle oversized packets in tcp and udp socket tests. #9904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handle oversized packets in tcp and udp socket tests. #9904
Conversation
@VeijoPesonen , this PR addresses ONME-4166. Would you please let me know which platforms should trigger this behavior, so we can test? @AriParkkila, perhaps you would also know? |
@michalpasztamobica, thank you for your changes. |
@@ -71,7 +71,9 @@ void TCPSOCKET_ECHOTEST() | |||
fill_tx_buffer_ascii(tcp_global::tx_buffer, BUFF_SIZE); | |||
|
|||
sent = sock.send(tcp_global::tx_buffer, pkt_s); | |||
if (sent < 0) { | |||
if (check_oversized_packets(sent, pkt_s)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about just sending the same amount of data but doing it in multiple chunks instead? We are anyway talking here about a stream instead of datagram.
If the modem returns error when trying to send too much data at a time it should also be handled.
Maybe instead of proactively recognizing "oversized" packets make the check if the driver returns error like NSAPI_ERROR_PARAMETER. I would say we want to have a clear indication packet is too big instead of dropping it silently, or what ever the driver might do instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check_oversized_packets() function does exactly what you said. It starts with:
if (error == NSAPI_ERROR_PARAMETER) {
I hid it away from the test code, as this is not a main point of any of our tests, but rather something we are forced to protect against. I think putting it all into test code would unnecessarily obfuscate it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, sorry that I didn't comprehend what I read :)
while (bt_left > 0) { | ||
sent = sock.send(&(tcp_global::tx_buffer[BURST_SIZE - bt_left]), bt_left); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the receiving be skipped totally if nothing got sent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would let the test recover if it is able to. Perhaps we should not treat the situation where the modem returns an error, due to oversized package as a critical one? We can just retry and let the test time out if the modem really can't do the job. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A TCP error is a fatal error. It means the stream has broken down. You can't get an error purely because of "I can't send this much data in one go". If the modem does do that, it's a fail.
(UDP case is totally different - there we do expect an EMSGSIZE-equivalent error for an attempt to send too large a packet. But NSAPI errors don't have a direct mapping for that)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conceptually, this arises from TCP being a reliable transport. It's not allowed to report random errors. The data goes through cleanly in its entirety, or the connection is lost. Plus the fact it's a stream - it doesn't preserve datagram boundaries, so not being able to send 1500 bytes in one call is not a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so what I really need to do is to rewrite all our TCP tests to match the pattern used in TCPSOCKET_ECHOTEST_BURST, as it is the only one that does the "just-send-the-remainder-and-don't-worry" part.
This particular test seems to do the job right, the other ones don't.
Thanks a lot for your remarks, @kjbracey-arm and @VeijoPesonen .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kjbracey-arm , do we also expect recv to work the same way? I just got the TCPSocket::recv to return 536 instead of 600 with K64F+ethernet, using LWIP...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I can see the documentation explains it well enough:
* By default, send blocks until all data is sent.
* By default, recv blocks until some data is received.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Asymmetry makes sense because for recv you don't (in general) know how much data you're expecting up-front - your size is usually the buffer size, not the expected data size. Plus you can often do something with partial data. Whereas for send, there's probably not a lot you can do after sending part of a buffer, if you've actually got all the data sitting there already in that buffer.
In full systems there is usually a MSG_WAITALL
socket option to change the receive behaviour to wait for the buffer to be filled.
We don't provide that, even though in Mbed OS it would actually be more useful than in POSIX.
In POSIX, the "wait all" doesn't actually necessarily work because any blocking call can be interrupted by a signal, so you often need to retry on EINTR
anyway. (This comes up a lot on Stack Overflow etc). We don't have that problem, so we can be confident that the send will do everything, as would MSG_WAITALL if we implemented it. (We could, in the TCPSocket layer).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, the catch is that MSG_WAITALL isn't actually a socket option, is it? It's a flag for recv
. And our recv
doesn't currently have flags - we'd need to extend it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All clear, thank you for the explanation. I update the tests to my best understanding of what we expect.
The blocking sends will now fail if the returned value is different from the expected one. (but the receives will loop until everything is received). No oversized checks are done in TCP tests, as that makes no sense.
Just waiting for @AriParkkila to let us know what is returned by the problematic modems in UDP tests.
TESTS/netsocket/tcp/main.cpp
Outdated
if (error == NSAPI_ERROR_PARAMETER) { | ||
if (get_ip_version() == NSAPI_IPv4) { | ||
if (size > tcp_global::MAX_SEND_SIZE_IPV4) { | ||
size = tcp_global::MAX_SEND_SIZE_IPV4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what you aim to do by modifying the size
variable.
The function did not send the data, so you should not claim that it did.
Please refactor this function to take just int size
parameter, so that it does not modify the original error code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to make sure that the next loop would not use oversized packet (we want to limit the size to 536, so that the modem accepts it).
Another approach that would perhaps be more obvious is to use the return value somehow. For example return 0 (NSAPI_ERROR_OK) if the packet was not oversized or return the suggested size otherwise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The modem can never reject a large TCP request (unless the TCP connection has broken). The underlying call may send a partial amount, which would be visible if you were using non-blocking. But that's always true. You just subtract off whatever they sent, then ask to send the remainder.
There should be no need to change the TCP tests specifically for modems with small IP MTUs, as the small IP MTU does not affect the TCP stream API. You do need to allow for limited UDP payloads.
Have you checked with modem that what do they return in case of TCP? I think the In case of non-blocking sockets, it is up to application responsibility to loop until all data has been sent. On blocking case, we already do that in |
@AriParkkila Can you comment here (or the internal ticket) what does the modem return if you try to |
38d7822
to
c3d3916
Compare
This review has been updated to address all the review remarks:
|
c3d3916
to
ebebfe8
Compare
Rebased and resolved conflicts. |
@kjbracey-arm , @VeijoPesonen , I would appreciate your approvals or suggestions for further improvements, so we can finalize this PR :) |
printf("[%02d] network error %d\n", i, sent); | ||
TEST_FAIL(); | ||
goto END; | ||
} else if (sent != BURST_SIZE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This set of conditions is inconsistent. If the call can return WOULDBLOCK
, it can return a partial length. Either they're both acceptable (if nonblocking) or they're both not (if blocking). I'm not sure whether you're using blocking or non-blocking sockets here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kjbracey-arm , thanks, this is a good point.
The test that you commented on is a blocking one, so I removed the check for WOULD_BLOCK - we can handle it just like any other error (an fail if it shows up).
There is a non-blocking version of the echo test below this one which is allowing a WOULD_BLOCK to happen and will just retry until timeout is exceeded.
I checked that the above is true for all tcp/tls and burst/non-burst combinations.
Test are still passing.
ebebfe8
to
266d4c4
Compare
CI started |
Test run: SUCCESSSummary: 6 of 6 test jobs passed |
…et_size_fix Handle oversized packets in tcp and udp socket tests.
Description
Some modems will not be able to handle IPv4 packets larger than 536 B and we need to cope with this in our tests.
My proposal is to provide a generic function for handling the situation, in order not to obfuscate the test code too much. The function will limit the size of the packets in TCP_ECHOTEST_BURST(), where the test is able to continue. We could rewrite the other tests to do the same, but I thought that perhaps it is not their purpose to do so - they will simply
Pull request type
Reviewers
@VeijoPesonen
@SeppoTakalo
@mtomczykmobica
@KariHaapalehto
@tymoteuszblochmobica
Release Notes
Not applicable.