From ea72c3d770dec0106de4cde31300b0abc320cfa5 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Fri, 8 Jun 2018 20:02:27 +0100 Subject: [PATCH] Limit outstanding CON requests to NSTART per session for congestion control https://tools.ietf.org/html/rfc7641#section-4.5 In order not to cause congestion, servers MUST strictly limit the number of simultaneous outstanding notifications/responses that they transmit to a given client to NSTART (1 by default; see Section 4.7 of RFC 7252 [RFC7252]). An outstanding notification/response is either a confirmable message for which an acknowledgement has not yet been received and whose last retransmission attempt has not yet timed out or a non-confirmable message for which the waiting time that results from the following rate-limiting rules has not yet elapsed. Delay sending of PDUs if active CON request waiting for ACK. Do not start of another Observer unsolicited response if active CON waiting waiting for ACK. Display outgoing PDU if log level DEBUG or higher to help troubleshooting. This does not fix the ratelimiting of non-confirmable messages, which should be handled by the application. --- include/coap/coap_session.h | 1 + src/net.c | 22 ++++++++++++++++++++-- src/resource.c | 2 ++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/include/coap/coap_session.h b/include/coap/coap_session.h index b3841d5c..060d9d3a 100644 --- a/include/coap/coap_session.h +++ b/include/coap/coap_session.h @@ -68,6 +68,7 @@ typedef struct coap_session_t { struct coap_context_t *context; /**< session's context */ void *tls; /**< security parameters */ uint16_t tx_mid; /**< the last message id that was used in this session */ + uint8_t con_active; /**< Active CON request sent */ struct coap_queue_t *sendqueue; /**< list of messages waiting to be sent */ size_t partial_write; /**< if > 0 indicates number of bytes already written from the pdu at the head of sendqueue */ uint8_t read_header[8]; /**< storage space for header of incoming message header */ diff --git a/src/net.c b/src/net.c index c7cbfa61..bd5ee83b 100644 --- a/src/net.c +++ b/src/net.c @@ -617,6 +617,9 @@ coap_session_send_pdu(coap_session_t *session, coap_pdu_t *pdu) { default: break; } + if (LOG_DEBUG <= coap_get_log_level()) { + coap_show_pdu(pdu); + } return bytes_written; } @@ -631,7 +634,12 @@ coap_send_pdu(coap_session_t *session, coap_pdu_t *pdu, coap_queue_t *node) { assert(session->endpoint != NULL); sock = &session->endpoint->sock; } + if (pdu->type == COAP_MESSAGE_CON) + session->con_active++; bytes_written = coap_socket_send_pdu(sock, session, pdu); + if (LOG_DEBUG <= coap_get_log_level()) { + coap_show_pdu(pdu); + } coap_ticks(&session->last_rx_tx); #else @@ -682,13 +690,17 @@ coap_send_pdu(coap_session_t *session, coap_pdu_t *pdu, coap_queue_t *node) { } } - if (session->state != COAP_SESSION_STATE_ESTABLISHED) + if (session->state != COAP_SESSION_STATE_ESTABLISHED || + (pdu->type == COAP_MESSAGE_CON && session->con_active >= COAP_DEFAULT_NSTART)) { return coap_session_delay_pdu(session, pdu, node); + } if ((session->sock.flags & COAP_SOCKET_NOT_EMPTY) && (session->sock.flags & COAP_SOCKET_WANT_WRITE)) return coap_session_delay_pdu(session, pdu, node); + if (pdu->type == COAP_MESSAGE_CON) + session->con_active++; bytes_written = coap_session_send_pdu(session, pdu); #endif /* WITH_LWIP */ @@ -901,6 +913,8 @@ coap_retransmit(coap_context_t *context, coap_queue_t *node) { debug("** %s tid=%d: retransmission #%d\n", coap_session_str(node->session), node->id, node->retransmit_cnt); + if (node->session->con_active) + node->session->con_active--; bytes_written = coap_send_pdu(node->session, node->pdu, node); if (bytes_written == COAP_PDU_DELAYED) { @@ -934,6 +948,8 @@ coap_retransmit(coap_context_t *context, coap_queue_t *node) { coap_handle_failed_notify(context, node->session, &token); } #endif /* WITHOUT_OBSERVE */ + if (node->session->con_active) + node->session->con_active--; /* And finally delete the node */ if (node->pdu->type == COAP_MESSAGE_CON && context->nack_handler) @@ -2070,8 +2086,10 @@ coap_dispatch(coap_context_t *context, coap_session_t *session, /* find transaction in sendqueue to stop retransmission */ coap_remove_from_queue(&context->sendqueue, session, pdu->tid, &sent); + if (session->con_active) + session->con_active--; if (pdu->code == 0) - goto cleanup; + goto cleanup; /* if sent code was >= 64 the message might have been a * notification. Then, we must flag the observer to be alive diff --git a/src/resource.c b/src/resource.c index 440feccc..f54311d7 100644 --- a/src/resource.c +++ b/src/resource.c @@ -712,6 +712,8 @@ coap_notify_observers(coap_context_t *context, coap_resource_t *r) { if (r->dirty == 0 && obs->dirty == 0) /* running this resource due to partiallydirty, but this observation's notification was already enqueued */ continue; + if (obs->session->con_active >= COAP_DEFAULT_NSTART) + continue; coap_tid_t tid = COAP_INVALID_TID; obs->dirty = 0;