net/nanocoap: Build message with coap_pkt_t#9085
Conversation
|
I plan to add a couple more tests, including for the Content-Format uint option. I think that then I can remove the WIP label. The coap_opt_get_string() function can be added to #8920. We can create a PR like #8920 for uint options. We also can create a follow-on PR for sorting options in coap_opt_finish() that were added provisionally. Certainly the gcoap adaptation will need this. |
68f3916 to
3aba9b3
Compare
|
Squashed WIP commits; ready for review. The unit tests provide decent coverage. To give the work some live testing, I created a CLI app based on the gcoap example. See my riot-nano-cli repo. As I pulled this together, a couple of issues arose in my mind:
|
|
@kaspar030, I assume that the basic approach here is acceptable. I am starting on a PR based on this one to adapt gcoap to it. This will allow us to remove the gcoap-specific #ifdefs added in #8772 and to reverse the stack size increase in #9000. |
634a7b1 to
a13c0ec
Compare
|
Squashed. No one has commented, and this simplifies a follow-on PR to sort options that I'm creating. |
sys/include/net/nanocoap.h
Outdated
| * @post pkt.payload advanced to first byte after option(s) | ||
| * @post pkt.payload_len reduced by option(s) length | ||
| * | ||
| * @param[inout] pkt pkt referencing target buffer |
| /** @} */ | ||
|
|
||
| /** | ||
| * @name coap_opt_finish() flag parameter values |
There was a problem hiding this comment.
A small description of the purpose of these flags would be helpful here.
sys/include/net/nanocoap.h
Outdated
| * @post pkt.payload advanced to first byte after option | ||
| * @post pkt.payload_len reduced by option length | ||
| * | ||
| * @param[inout] pkt pkt referencing target buffer |
| * | ||
| * @return number of bytes written to buffer | ||
| */ | ||
| ssize_t coap_opt_add_string(coap_pkt_t *pkt, uint16_t optnum, const char *string, char separator); |
There was a problem hiding this comment.
Is it possible for this function to return error codes? If not, can we change the return type to size_t instead of ssize_t?
There was a problem hiding this comment.
This is an interesting API design question. These coap_opt_add...() functions rely on _add_opt_pkt(), which in turn relies on coap_put_option(). IMO the latter can be improved to return -ENOSPC because the user can't always determine in advance if the option will be too long for the available buffer space. See the questions I pose in a comment above.
So, I'm sort of anticipating that we will update these functions eventually to return an error code in that case. Does it make sense to update the API now for these new functions so the user does not need to update their code in the future? Besides, use of a signed value will never really be a problem in this case. Why box in the API if you don't need to?
OTOH, maybe this whole idea of overrunning the buffer when writing options is so unlikely that the present use of an assert after the option is added is the best/simplest solution.
There was a problem hiding this comment.
ssize_t sounds good to me. I agree with you reason that these function might start returning errors. This error could not only be caused by a buffer overflow with the pdu buffer space, but also with the option array being completely filled.
I tend to favor a "soft" error return code here over an assertion. Mostly because it is possible for external CoAP requests to influence these options, and it would be bad if an assertion could be remotely triggered with some specific request (causing a packet of death vulnerability).
I think it would be best to add an @return negative on error to the doxygen both here and with the other "coap_opt_add" functions to indicate the need for error checking.
There was a problem hiding this comment.
Please see the exact wording and let me know if any concerns. Also I updated the implementation of coap_opt_add_string() to return -ENOSPC. The input string may contain multiple separators, and it might be difficult for the caller to determine this in advance.
Mostly because it is possible for external CoAP requests to influence these options
Can you be more specific? I'm not sure that I follow you.
| * | ||
| * @return number of bytes written to buffer | ||
| */ | ||
| ssize_t coap_opt_add_uint(coap_pkt_t *pkt, uint16_t optnum, uint32_t value); |
There was a problem hiding this comment.
Same with the return type as for coap_opt_add_string
sys/include/net/nanocoap.h
Outdated
| * @post pkt.payload advanced to first available byte after options | ||
| * @post pkt.payload_len is maximum bytes available for payload | ||
| * | ||
| * @param[inout] pkt pkt to update |
| * | ||
| * @return total number of bytes written to buffer | ||
| */ | ||
| ssize_t coap_opt_finish(coap_pkt_t *pkt, uint16_t flags); |
There was a problem hiding this comment.
How many flags are we expecting here, would uint8_t suffice?
There was a problem hiding this comment.
Might we need more than eight flags? Another interesting API question. We already have used two (add-payload, and sort-options in another PR) I rounded up here figuring it didn't really matter in terms of space, and it allows for future flexibility. Conceivably we also could use the high-order byte if some future flag requires an associated value.
There was a problem hiding this comment.
Ok. uint16_t sounds good then. Could you please reflect this in the flag defines, so 0x0001 instead of 0x1 to have the data size a bit more explicit there.
| uint16_t lastonum = (pkt->options_len) | ||
| ? pkt->options[pkt->options_len - 1].opt_num : 0; | ||
| assert(optnum >= lastonum); | ||
|
|
There was a problem hiding this comment.
I think you should check here whether there is still an empty position in the options array, to prevent array-out-of-bounds bugs
| * @param[in] len length of buf | ||
| * @param[in] header_len length of header in buf, including token | ||
| */ | ||
| void coap_pkt_init(coap_pkt_t *pkt, uint8_t *buf, size_t len, size_t header_len); |
There was a problem hiding this comment.
I think an explicit @pre to indicate that there should be an initialized coap_hdr_t in the buffer would be nice. Somebody is going to miss the note with the buf param there :)
|
Thanks for the review, @bergzand. Pushed fixups. |
|
I believe I've addressed @bergzand's comments. It would be nice to at least have a high-level "ok with the approach" confirmation from @kaspar030 since this change is a significant addition to nanocoap. It is a new API to build a message instead of the historical, purely buffer-based approach. |
|
@kaspar030 On a samr21-xpro, this patch adds 26 bytes of flash usage to the nanocoap-server example, that is still unmodified without any usage of these functions. |
|
@kb2ma The approach looks good to me -> high-level ACK. |
|
@bergzand, thanks for measuring. I plan to review and report. I haven't tried cosy yet. @kaspar030, thanks for confirmation. |
|
@kb2ma I'd like to test this a bit more and then ACK it. Feel free to squash in the mean time. |
Includes string and uint options.
ca3da41 to
aa0d02e
Compare
|
Rebased and squashed. Relative to master, I see the difference in size of the text sections as 52 bytes. I attribute this to
In the proposed module doc for nanocoap, I include macros to exclude the code (COAP_WRITE_OPTIONS_STRUCT), which clears up this problem. If this is important to do now, I can add to this PR, or we can put it in a follow-on PR. |
Let's do this in a follow up. |
Contribution description
Adds an API to nanocoap to use a coap_pkt_t to build a message, including options. Builds on existing buffer-based functions. Use of the coap_pkt_t struct makes it easier to manage options.
Includes:
This approach also allows integration by gcoap, bringing the APIs closer together.
Issues/PRs references
Implements ideas described in #9048 and #8831. Provides an API useful to #8920.