-
Notifications
You must be signed in to change notification settings - Fork 2k
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
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 |
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 should be @param[in,out]
according to doxygen
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.
Sure thing.
@@ -224,6 +225,15 @@ extern "C" { | |||
#define COAP_BLOCKWISE_SZX_MAX (7) | |||
/** @} */ | |||
|
|||
/** | |||
* @name coap_opt_finish() flag parameter values |
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 small description of the purpose of these flags would be helpful 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.
Will do.
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 |
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.
Same as above
sys/include/net/nanocoap.h
Outdated
* | ||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
sys/include/net/nanocoap.h
Outdated
* | ||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
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.
[in,out]
* | ||
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How many flags are we expecting here, would uint8_t
suffice?
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
Done.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should check here whether there is still an empty position in the options array, to prevent array-out-of-bounds bugs
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.
Excellent idea!
* @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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 :)
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 agree completely.
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. |
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.
Ack
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.