0

I am developing a CoAP client on the nRF9160 DK, running Zephyr RTOS. I am having trouble with longer Proxy-URI's; short URIs (268 characters and below) work fine and the coap message reaches the server as expected. However, messages with longer Proxy-URIs (269 characters and above) fail to go through for some reason. For example, with the following initialisation:

uint8_t tx_coap_buf[2048];
err = coap_packet_init(&request, tx_coap_buf, sizeof(tx_coap_buf), APP_COAP_VERSION, COAP_TYPE_CON, sizeof(next_token), (uint8_t *) &next_token, COAP_METHOD_POST, next_id);
if (err < 0) {
    LOG_DBG("Failed to create CoAP request, %d", err);
    return err;
}

The below (short) works fine

char * proxy_uri = "http://127.0.0.1:3000/abc/europe-xyz1/coap-abc/abc-device/publishEvent?jwt=eyJ0eXAiO";
ssize_t proxy_uri_len = strlen(proxy_uri);
err = coap_packet_append_option(&request, COAP_OPTION_PROXY_URI, proxy_uri, proxy_uri_len);
if (err < 0) {
    LOG_DBG("Failed to create CoAP request, %d", err);
    return err;
}

But this one (longer) doesn't, even though err returns as 0.

char * proxy_uri = "http://127.0.0.1:3000/abc/europe-xyz1/coap-abc/abc-device/publishEvent?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJhaXPocmlzIiwiaXNzJjoiYXV0aDAiLCJleHTiOjE2MDk0Nzc1NTUsImlhdCI6MTYwOTQ2Njc1MX2.RBs-SSa8x9VpyvBRw_EA2CUihgle5yGDJa8f2DUoGXe8d1Vah6bABILZuuyFQXcEg0Mh1BLn1p6qmbwb8BnsNg";
ssize_t proxy_uri_len = strlen(proxy_uri);
err = coap_packet_append_option(&request, COAP_OPTION_PROXY_URI, proxy_uri, proxy_uri_len);
if (err < 0) {
    LOG_DBG("Failed to create CoAP request, %d", err);
    return err;
}

...and when I inspect the CoAP message using Wireshark, the Proxy-URI option has the warning: Expert Info (Warning/Malformed): option longer than the package

I tried setting the additional Zephyr CoAP config as follows

CONFIG_COAP_EXTENDED_OPTIONS_LEN=y
CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE=800

...but had no luck.

Would anyone know what I could be missing? Is there some CoAP config whose default value I need to override so as to accommodate longer Proxy-URI options?

Thanks.

Michael-7
  • 1,739
  • 14
  • 17

3 Answers3

2

The limit 268/269 is the threshold, where the option length is encoded with 1/2 bytes. Maybe, it's just a bug with such "large options" in the library used there.

Just as experiment (it doesn't work finally with the proxy you used in that tutorial), try to use instead of the "huge" COAP_OPTION_PROXY_URI a combination of COAP_OPTION_PROXY_SCHEME (http) and split the rest of the url into COAP_OPTION_URI_HOST, COAP_OPTION_URI_PATH, COAP_OPTION_URI_QUERY. That should result then smaller options than 269 (hopefully). With that, check, what wireshark displays. If wireshark is OK, add that hint with the option length to the question in the nordic forum. If you still don't get an answer there, please open an issue in Eclipse/Californium and I will see, what will be required for the current proxy2 implementation to work for that cloud API of that tutorial.

(Note: the URI "http://127.0.0.1:3000/abc/europe-xyz1/coap-abc/abc-device/publishEvent?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJhaXPocmlzIiwiaXNzJjoiYXV0aDAiLCJleHTiOjE2MDk0Nzc1NTUsImlhdCI6MTYwOTQ2Njc1MX2.RBs-SSa8x9VpyvBRw_EA2CUihgle5yGDJa8f2DUoGXe8d1Vah6bABILZuuyFQXcEg0Mh1BLn1p6qmbwb8BnsNg" will then be:

COAP_OPTION_PROXY_SCHEME=http
COAP_OPTION_URI_HOST=127.0.0.1
COAP_OPTION_URI_PORT=3000
COAP_OPTION_URI_PATH=abc
COAP_OPTION_URI_PATH=europe-xyz1
COAP_OPTION_URI_PATH=coap-abc
COAP_OPTION_URI_PATH=abc-device
COAP_OPTION_URI_PATH=publishEvent
COAP_OPTION_URI_QUERY=jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJhaXPocmlzIiwiaXNzJjoiYXV0aDAiLCJleHTiOjE2MDk0Nzc1NTUsImlhdCI6MTYwOTQ2Njc1MX2.RBs-SSa8x9VpyvBRw_EA2CUihgle5yGDJa8f2DUoGXe8d1Vah6bABILZuuyFQXcEg0Mh1BLn1p6qmbwb8BnsNg

)

Achim Kraus
  • 729
  • 1
  • 7
  • 11
  • Thanks Achim. I managed to stay well within the 268 limit by renaming path segments, and removing redundant fields in the "claims" before generating the JWT. I didn't get to try your suggestion but am convinced wireshark will be ok with that reformatting - I was able to include other options beside the shortened proxy-uri, and everything still worked as expected. – Michael-7 Jan 09 '21 at 22:26
  • I shall ping both the Nordic and Zephyr teams to get confirmation of what you suspect, and update the thread accordingly. Thanks for the tip! – Michael-7 Jan 09 '21 at 22:29
  • Great news. Just one question, which version of the ncs do you use? 1.4.1? If not, maybe you update and test it again before raising an issue. – Achim Kraus Jan 10 '21 at 08:42
  • Yes, I am using NCS 1.4.1. This could very well be a bug - I noticed the value 269 being used in Zephyr's coap.c library file. – Michael-7 Jan 10 '21 at 09:34
  • I already tried yesterday to find the bug there, but for me it looks well. Otherwise this experience points to an bug. Would you please add that information to the question in the forum? – Achim Kraus Jan 10 '21 at 09:41
2

Got it! Version 1.4.1, coap.c, line 221, uses "delta_size" instead of "len_size".

if (len_size == 1U) {
    res = append_u8(cpkt, (uint8_t)len_ext);
    if (!res) {
        return -EINVAL;
    }
} else if (delta_size == 2U) {
    res = append_be16(cpkt, len_ext);
    if (!res) {
        return -EINVAL;
    }
}

I add this to your question in the forum.

And https://github.com/zephyrproject-rtos/zephyr/issues/31206

Achim Kraus
  • 729
  • 1
  • 7
  • 11
0

CoAP messages are typically limited by the application's buffer in size, and practically limited to one MTU (as IP fragmentation is rarely used together with CoAP). Unlike the message's payload, an option can not be split across multiple messages using block-wise transfer. On the Zephyr side, the error went unnoticed because you discarded the coap_packet_append_option result code.

For the concrete case of this URI, you can work around the limitation using a larger message buffer (how that is done depends on how you initialized request in the first place).


Note that transporting a JWT in the URI for authentication purposes is not how authentication is typically done in CoAP applications. If your HTTP server accepts TLS client certificates instead of CWTs, you may consider provisioning the proxy with a suitable client certificate (to be used when the CoAP client is authenticated properly, eg. using DTLS or OSCORE), and then use only the almost reasonably sized token-less path.


[edit after question clarification]

The lack of errors indicates a flaw in the Zephyr code, either Zephyr itself or the concrete network interface used (which should not silently truncate outgoing messages without letting the OS know), and would best be addressed inside the Zephyr issue tracker.

chrysn
  • 810
  • 6
  • 19
  • 1
    A slightly different idea would be to send such tokens in a specific coap-option and store it within the coap peer's dtls context in the proxy (requires support by the implementation :-) ). Later request in that dtls context without that option may then add that token as "http authorization: Bearer ". Agreeing, that JWT are not the optimal solution for coap itself, it seems, that a coap2http proxy requires such a mechanism for too many current APIs. – Achim Kraus Jan 08 '21 at 09:05
  • Hi @chrysn and Achim, thanks for your answers. I have edited my question to clarify that the return codes are good. I have also included a snippet to show how the request is initialised. The entire message size is well under 1024 bytes, which as I understand should fit into a single UDP MTU. Basically, I am following the approach used by Google in their CoAP proxy - https://cloud.google.com/community/tutorials/cloud-iot-coap-proxy, and they seem to have sample apps using the same approach (though not using Zephyr) - https://youtu.be/MpkdokSyQIE?t=1856. A Java CoAP client works fine. – Michael-7 Jan 08 '21 at 13:57
  • 1
    I've seen your question in the modem's manufacturer forum. I guess, there is something configured, which truncates your message (as Christian already wrote). Do you use an example for that modem? Which one? And, may be, it helps to ping on your question in the modem's forum. – Achim Kraus Jan 08 '21 at 17:13
  • 1
    You are correct, I also posted on NordicSemi.com. I adapted their coap_client example for the nRF9160, adding DTLS and communicating with a Google CoAP Proxy instance (though at present I am testing everything locally). I have combed back and forth, but can't find what that config may be. – Michael-7 Jan 08 '21 at 22:08
  • 1
    Some compressed form of adding a `HTTP Authorization: Bearer ` from the DTLS context, as Achim suggested, would indeed be a viable workaround, but quite a hassle to implement (as it'd be a CoAP operation on the individual DTLS connection that's not visible to the CoAP layer). A possible synthesis of his and my approach could be to have a proxy that recognizes the client's DTLS certificate (or PSK) and then, by its configuration (and not introspection of the DTLS context) put the matching JWT on every outgoing request towards a particular HTTP server. – chrysn Jan 09 '21 at 10:31
  • The proposal shoud mainly affect the coap2http spec. There it may be either just passed into the http-header (as other coap-options are also passed). And, because it is usually a little larger, there may be a "caching" mechanism used in order to recude the traffic. I would bind that cache to the dtls session (or similiar). Just to remind, coap already binds the request/response matching to dtls, so the dtls-layer is already visible. – Achim Kraus Jan 09 '21 at 13:44
  • Thanks for the suggestions, I will give it a close look and see how these can be incorporated. The challenge I would guess might be simultaneously avoiding too much state in the Proxy, as well as keeping it free from explicit knowledge of the different devices. – Michael-7 Jan 09 '21 at 22:17
  • If coaps is used, there is already state. This state could be removed and recreated without too much trouble. Just to mention: I currently implemented a "graceful restart", means Californium keeps the DTLS state during a restart. And, if DTLS 1.2 CID is used, Californium has already a "built-in cid cluster support". But that requires to update the proxy. – Achim Kraus Jan 10 '21 at 08:50
  • The state is there, it's just hard to address because when CoAP-over-TCP/TLS was specified, the 7.xx codes were not extended to apply to DTLS connections (even though that would make sense). A message 7.01 CSM with a new option Authorizatino-Token-For: [origin, JWT] CBOR encoded option could do that efficiently -- it just still doesn't solve the original problem that messages containing the token are long enough to run into a bug in the setup. – chrysn Jan 10 '21 at 11:40
  • In Californium we added a flexible request/response matching in order to handle "unaware" DTLS handshakes (e.g. just before the response is passed to DTLS). Therefore it passes the (dtls) endpoint-context up to the coap-, and even the application-layer and use it for sent back messages to ensure, it's sent back to the right peer. With that it's easy to extend the proxy to cache some options with the dtls session id as lookup key. Therefore I wrote "requires support by the implementation". – Achim Kraus Jan 10 '21 at 16:19