-
Notifications
You must be signed in to change notification settings - Fork 6.8k
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: coap: Fix response matching algorithm #84160
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1808,6 +1808,8 @@ struct coap_reply *coap_response_received( | |
{ | ||
struct coap_reply *r; | ||
uint8_t token[COAP_TOKEN_MAX_LEN]; | ||
bool piggybacked = false; | ||
uint8_t type; | ||
uint16_t id; | ||
uint8_t tkl; | ||
size_t i; | ||
|
@@ -1817,23 +1819,44 @@ struct coap_reply *coap_response_received( | |
return NULL; | ||
} | ||
|
||
type = coap_header_get_type(response); | ||
id = coap_header_get_id(response); | ||
tkl = coap_header_get_token(response, token); | ||
|
||
if (type == COAP_TYPE_ACK || type == COAP_TYPE_RESET) { | ||
piggybacked = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A RST is always a empty message and no piggybacked response. See RFC 7252, page 23, table 1
|
||
} | ||
|
||
for (i = 0, r = replies; i < len; i++, r++) { | ||
int age; | ||
|
||
if ((r->id == 0U) && (r->tkl == 0U)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the message ID 0 used to indicate something special in the zephyr implementation? FMPOV, it's wrong. I don't see this is related to RFC 7252. |
||
continue; | ||
} | ||
|
||
/* Piggybacked must match id when token is empty */ | ||
if ((r->id != id) && (tkl == 0U)) { | ||
continue; | ||
} | ||
if (piggybacked) { | ||
/* In a piggybacked response, the Message ID of the | ||
* Confirmable request and the Acknowledgment MUST | ||
* match, and the tokens of the response and original | ||
* request MUST match. | ||
*/ | ||
if ((r->id != id || (r->tkl != tkl))) { | ||
continue; | ||
} | ||
|
||
if (tkl > 0 && memcmp(r->token, token, tkl)) { | ||
continue; | ||
if (r->tkl > 0) { | ||
if (memcmp(r->token, token, r->tkl) != 0) { | ||
continue; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
will use less code-duplicates. |
||
} else { | ||
/* In a separate response, just the tokens of the | ||
* response and original request MUST match. | ||
*/ | ||
if ((r->tkl == 0) || (r->tkl != tkl) || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the code-duplicates are intended, then the
|
||
(memcmp(r->token, token, r->tkl) != 0)) { | ||
continue; | ||
} | ||
} | ||
|
||
age = coap_get_option_int(response, COAP_OPTION_OBSERVE); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1855,4 +1855,87 @@ ZTEST(coap, test_age_is_newer) | |
"Max age should be marked as newer"); | ||
} | ||
|
||
struct test_coap_request { | ||
uint16_t id; | ||
uint8_t token[COAP_TOKEN_MAX_LEN]; | ||
uint8_t tkl; | ||
enum coap_msgtype type; | ||
struct coap_reply *match; | ||
}; | ||
|
||
static int reply_cb(const struct coap_packet *response, | ||
struct coap_reply *reply, | ||
const struct sockaddr *from) | ||
{ | ||
return 0; | ||
} | ||
|
||
ZTEST(coap, test_response_matching) | ||
{ | ||
struct coap_reply matches[] = { | ||
{ .id = 100, .reply = reply_cb }, | ||
{ .id = 101, .token = { 1, 2, 3, 4 }, .tkl = 4, .reply = reply_cb }, | ||
}; | ||
struct test_coap_request test_responses[] = { | ||
/* Piggybacked ACK, no token */ | ||
{ .id = 100, .type = COAP_TYPE_ACK, .match = &matches[0] }, | ||
/* Piggybacked ACK, matching token */ | ||
{ .id = 101, .type = COAP_TYPE_ACK, .match = &matches[1], | ||
.token = { 1, 2, 3, 4 }, .tkl = 4 }, | ||
/* Piggybacked ACK, token mismatch */ | ||
{ .id = 101, .type = COAP_TYPE_ACK, .match = NULL, | ||
.token = { 1, 2, 3, 3 }, .tkl = 4 }, | ||
/* Piggybacked ACK, token mismatch 2 */ | ||
{ .id = 100, .type = COAP_TYPE_ACK, .match = NULL, | ||
.token = { 1, 2, 3, 4 }, .tkl = 4 }, | ||
/* Piggybacked ACK, token mismatch 3 */ | ||
{ .id = 101, .type = COAP_TYPE_ACK, .match = NULL }, | ||
/* Piggybacked ACK, id mismatch */ | ||
{ .id = 102, .type = COAP_TYPE_ACK, .match = NULL, | ||
.token = { 1, 2, 3, 4 }, .tkl = 4 }, | ||
/* Separate reply, no token */ | ||
{ .id = 100, .type = COAP_TYPE_CON, .match = NULL }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For separate replies, there is no relation in the MIDs! I'm not common with the used test framework, but for me this piggybacked response matches the first reply ".id = 100," (empty token). "no token" never applies, that case is called "empty token" and all rules for tokens applies for empty tokens as well. |
||
/* Separate reply, no token 2 */ | ||
{ .id = 101, .type = COAP_TYPE_CON, .match = NULL }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. separate response, MID must be ignored, empty token matches first reply. |
||
/* Separate reply, matching token 1 */ | ||
{ .id = 101, .type = COAP_TYPE_CON, .match = &matches[1], | ||
.token = { 1, 2, 3, 4 }, .tkl = 4 }, | ||
/* Separate reply, matching token 2 */ | ||
{ .id = 102, .type = COAP_TYPE_CON, .match = &matches[1], | ||
.token = { 1, 2, 3, 4 }, .tkl = 4 }, | ||
/* Separate reply, token mismatch */ | ||
{ .id = 101, .type = COAP_TYPE_CON, .match = NULL, | ||
.token = { 1, 2, 3, 3 }, .tkl = 4 }, | ||
/* Separate reply, token mismatch 2 */ | ||
{ .id = 100, .type = COAP_TYPE_CON, .match = NULL, | ||
.token = { 1, 2, 3, 3 }, .tkl = 4 }, | ||
}; | ||
|
||
ARRAY_FOR_EACH_PTR(test_responses, response) { | ||
struct coap_packet response_pkt = { 0 }; | ||
struct sockaddr from = { 0 }; | ||
struct coap_reply *match; | ||
uint8_t data[64]; | ||
int ret; | ||
|
||
ret = coap_packet_init(&response_pkt, data, sizeof(data), COAP_VERSION_1, | ||
response->type, response->tkl, response->token, | ||
COAP_RESPONSE_CODE_CONTENT, response->id); | ||
zassert_ok(ret, "Failed to initialize test packet: %d", ret); | ||
|
||
match = coap_response_received(&response_pkt, &from, matches, | ||
ARRAY_SIZE(matches)); | ||
if (response->match != NULL) { | ||
zassert_not_null(match, "Did not found a response match when expected"); | ||
zassert_equal_ptr(response->match, match, | ||
"Wrong response match, test %d match %d", | ||
response - test_responses, match - matches); | ||
} else { | ||
zassert_is_null(match, | ||
"Found unexpected response match, test %d match %d", | ||
response - test_responses, match - matches); | ||
} | ||
} | ||
} | ||
|
||
ZTEST_SUITE(coap, NULL, NULL, NULL, NULL, NULL); |
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 ! before
is_empty_message
is wrong. Both requests and empty messages can't be responses, see table above.