obgm/libcoap

Q-Block support detection fails in some cases

Closed this issue · 5 comments

Environment

  • Build System: [CMake]
  • Operating System: [Linux]
  • Operating System Version: [N/A]
  • Hosted Environment: [None]

libcoap Configuration Summary

If get_config,sh exists, please copy the output from (do in the top level libcoap directory) :-

PACKAGE VERSION..................4.3.4
PACKAGE SOURCE...................f26c2d0-dirty
LIBRARY API VERSION..............3
LIBRARY ABI VERSION..............3.1.2
ENABLE_DTLS:.....................ON
ENABLE_TCP:......................ON
ENABLE_IPV4:.....................ON
ENABLE_IPV6:.....................ON
ENABLE_AF_UNIX:..................ON
ENABLE_WEBSOCKETS:...............OFF
ENABLE_Q_BLOCK:..................ON
ENABLE_CLIENT_MODE:..............ON
ENABLE_SERVER_MODE:..............ON
ENABLE_OSCORE:...................ON
ENABLE_ASYNC:....................ON
ENABLE_DOCS:.....................OFF
ENABLE_EXAMPLES:.................OFF
DTLS_BACKEND:....................default
WITH_GNUTLS:.....................ON
WITH_TINYDTLS:...................OFF
WITH_OPENSSL:....................OFF
WITH_MBEDTLS:....................OFF
HAVE_LIBTINYDTLS:................
HAVE_LIBGNUTLS:..................1
HAVE_LIBOPENSSL:.................
HAVE_LIBMBEDTLS:.................
WITH_EPOLL:......................ON
WITH_OBSERVE_PERSIST:............ON
BUILD_SHARED_LIBS:...............ON
MAX_LOGGING_LEVEL:...............8
WARNING_TO_ERROR:................OFF
CMAKE_C_COMPILER:................/usr/bin/cc
CMAKE_CXX_COMPILER_ID:...........GNU
CMAKE_BUILD_TYPE:................Release
CMAKE_SYSTEM_PROCESSOR:..........x86_64
CMAKE_HOST_SYSTEM_NAME:..........Linux
CMAKE_GENERATOR:.................Unix Makefiles

Problem Description

When using CoAP client configured with COAP_BLOCK_TRY_Q_BLOCK it will try to request the root (/) resource from the server with Q-Block2 option and based on the response decide whether Q-Block functionality is supported by the server. That goes wrong in some cases:

  • The first and the most obvious problem occurs when the server does not have a root resource configured. I'm not aware of the requirement that the root resource must always exist (please correct me if I'm wrong).
  • The second issue is that even when the server does support Q-Block it will not necessarily return Q-Block2 option in the response. RFC9177 Section 4.2 says that the absence of the option is equivalent to the option value with block index = 0, M = 0 and block size unspecified. To me that implies that it is valid to not have the option in the response if it's not necessary (that is when the whole response body is smaller than the block size).

Additionally, this whole detection mechanism becomes unnecessary hassle if you know for a fact that the server does support Q-Block and you would prefer libcoap to just go with that assumption without any probes.

Expected Behavior

I would like to be able to use Q-Block functionality without having to jump trough hoops by implementing an additional resource on the server side for the sole purpose of pleasing the client's detection mechanism and then having the client waste time for the unnecessary detection.

Actual Behavior

The client concludes that the server does not support Q-Block if the root resource does not exists or is returned without Q-Block2 option.

Steps to reproduce

  1. coap_context_set_block_mode(context, ...|COAP_BLOCK_TRY_Q_BLOCK) - both on the server and client side.
  2. Make sure the server does not have a resource for empty Uri-Path registered or the corresponding GET handler uses coap_add_data(...) (as opposed to coap_add_data_large_response(...)) to compose the response.
  3. Send any request and observe the Q-Block detection kick in (and fail) before the actual request is sent.

Debug Logs

Testing for Q-Block support
v:1 t:CON c:GET i:7457 {01} [ Q-Block2:0/_/16 ]
v:1 t:ACK c:2.05 i:7457 {01} [ Content-Format:text/plain ] :: 'Dummy'
Q-Block support not available

Suggested Resolution

  1. Change the detection logic to not interpret successful response without Q-Block2 option as the lack of Q-Block support. To my understanding, since both Q-Block1 and Q-Block2 options are classified as critical, the only conclusive indication of the lack of support would be a "Bad Option" response. Any successful response would indicate the presence of support, regardless of the presence of the option in the response.
  2. Consider falling back to another URI (perhaps, /.well-known/core) in case of "Not Found" response for the root.
  3. Add a possibility to tell libcoap to assume Q-Block support without probing. The easiest way to do that would probably be moving COAP_BLOCK_HAS_Q_BLOCK to a public header and having coap_context_set_block_mode() accept it. However, this "everything or nothing" approach may not be good enough for clients that need to talk to different servers at the same time. In that case ability to set the flag per session would be helpful.

The first and the most obvious problem occurs when the server does not have a root resource configured.

Agreed that / does not have to be there. Will update this to .well-known/core which should always be there.

The second issue is that even when the server does support Q-Block it will not necessarily return Q-Block2 option in the response.

That should be fine - I will check things later. However, as Q-Block1 is set to be Critical, a Server that does not recognize or support Q-Block1 MUST send back a 4.02 as per RFC7252 5.4.1 Critical/Elective so server support should be properly detected.

Unrecognized options of class "critical" that occur in a
Confirmable request MUST cause the return of a 4.02 (Bad Option)
response.  This response SHOULD include a diagnostic payload
describing the unrecognized option(s) (see [Section 5.5.2]). 

If a server supports Q-Block, but does not have the / resource, the client will not detect Q-Block support as there will be a 4.04 response. Code will be updated to use /.well-known/core instead which should always be there and can be used for testing Q-Block support.

When testing for Q-Block support, the client uses the Q-Block2 option configured as block 0, block size 16 bytes, with the More bit unset to indicate that it is asking for for block 0 only (not the entire payload). Note that this is different to the Block2 option. It is very unlikely that the /.well-known/core response will be 16 or less bytes, so the server will be forced to respond using Q-Block2 (assuming supported) which the client then detects. So this part of the code is working as expected.

If a server receives a Q-Block option that it does not support (when using CON), as it is a critical option, it MUST respond with a 4.02 (RFC7252 5.4.1. Critical/Elective.) There are however CoAP servers that subscribe to "be tolerant of issues" and ignore options they do not understand/support. But here, they are not responding with a Q-Block2, so it is safe to assume they do not support Q-Block.

It is possible to add in a session only setting of a flag to indicate that the server supports Q-Block no matter what. However if a server does not support Q-Block (and silently ignores it) certainly usage of Q-Block1 by the client will not work as expected. Is this flexibility actually required?

It is possible to add in a session only setting of a flag to indicate that the server supports Q-Block no matter what. However if a server does not support Q-Block (and silently ignores it) certainly usage of Q-Block1 by the client will not work as expected. Is this flexibility actually required?

I was looking for this possibility mostly as a way to sidestep the issue with the automatic detection. If the latter works reliably I guess there is no real need for manual override. Skipping the detection might save some time on the client side, but I doubt that it will be of much importance in practice.

I was looking for this possibility mostly as a way to sidestep the issue with the automatic detection.

Make sense. #1372 has now been merged. Can this issue be closed now?

As far as I am concerned, yes.