mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-11-06 02:27:15 +00:00
altcp_tls_mbedtls: improve sent/recved handling
This commit is contained in:
parent
1ddb125e2c
commit
a2bc02d682
@ -35,21 +35,22 @@
|
||||
*
|
||||
* Author: Simon Goldschmidt <goldsimon@gmx.de>
|
||||
*
|
||||
* Missing things / @todo:
|
||||
* - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
|
||||
* the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
|
||||
* connection; altcp_recved() from inner connection does nothing)
|
||||
* - TX data is marked as 'sent' (i.e. acknowledged; sent callback is called) right
|
||||
* after enqueueing for transmission, not when actually ACKed be the remote host.
|
||||
* - Client connections starting with 'connect()' are not handled yet...
|
||||
* - some unhandled things are caught by LWIP_ASSERTs...
|
||||
* - only one mbedTLS configuration is supported yet (i.e. one certificate, settings, etc.)
|
||||
* Watch out:
|
||||
* - 'sent' is always called with len==0 to the upper layer. This is because keeping
|
||||
* track of the ratio of application data and TLS overhead would be too much.
|
||||
*
|
||||
* Configuration:
|
||||
* Mandatory security-related configuration:
|
||||
* - define ALTCP_MBEDTLS_RNG_FN to a custom GOOD rng function returning 0 on success:
|
||||
* int my_rng_fn(void *ctx, unsigned char *buffer , size_t len)
|
||||
* - define ALTCP_MBEDTLS_ENTROPY_PTR and ALTCP_MBEDTLS_ENTROPY_LEN to something providing
|
||||
* GOOD custom entropy
|
||||
*
|
||||
* Missing things / @todo:
|
||||
* - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
|
||||
* the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
|
||||
* connection; altcp_recved() from inner connection does nothing)
|
||||
* - Client connections starting with 'connect()' are not handled yet...
|
||||
* - some unhandled things are caught by LWIP_ASSERTs...
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
@ -107,7 +108,7 @@ struct altcp_tls_config
|
||||
|
||||
static err_t altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn);
|
||||
static void altcp_mbedtls_dealloc(struct altcp_pcb *conn);
|
||||
static err_t altcp_mbedtls_handle_rx_data(struct altcp_pcb *conn);
|
||||
static err_t altcp_mbedtls_handle_rx_data(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
|
||||
static int altcp_mbedtls_bio_send(void* ctx, const unsigned char* dataptr, size_t size);
|
||||
|
||||
|
||||
@ -157,6 +158,17 @@ altcp_mbedtls_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/* Call recved for possibly more than an u16_t */
|
||||
static void
|
||||
altcp_mbedtls_lower_recved(struct altcp_pcb *inner_conn, int recvd_cnt)
|
||||
{
|
||||
while (recvd_cnt > 0) {
|
||||
u16_t recvd_part = (u16_t)LWIP_MIN(recvd_cnt, 0xFFFF);
|
||||
altcp_recved(inner_conn, recvd_part);
|
||||
recvd_cnt -= recvd_part;
|
||||
}
|
||||
}
|
||||
|
||||
/** Recv callback from lower connection (i.e. TCP)
|
||||
* This one mainly differs between connection setup/handshake (data is fed into mbedTLS only)
|
||||
* and application phase (data is decoded by mbedTLS and passed on to the application).
|
||||
@ -231,14 +243,18 @@ altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p
|
||||
|
||||
if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
|
||||
/* handle connection setup (handshake not done) */
|
||||
int ret;
|
||||
int ret = mbedtls_ssl_handshake(&state->ssl_context);
|
||||
/* try to send data... */
|
||||
altcp_output(conn->inner_conn);
|
||||
if (state->bio_bytes_read) {
|
||||
/* acknowledge all bytes read */
|
||||
altcp_mbedtls_lower_recved(inner_conn, state->bio_bytes_read);
|
||||
state->bio_bytes_read = 0;
|
||||
}
|
||||
|
||||
/* during handshake: mark all data as received */
|
||||
altcp_recved(conn->inner_conn, p->tot_len);
|
||||
|
||||
ret = mbedtls_ssl_handshake(&state->ssl_context);
|
||||
if(ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
|
||||
/* handshake not done, wait for more recv calls */
|
||||
LWIP_ASSERT("in this state, the rx chain should be empty", state->rx == NULL);
|
||||
return ERR_OK;
|
||||
}
|
||||
if (ret != 0) {
|
||||
@ -260,21 +276,48 @@ altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p
|
||||
return ERR_OK;
|
||||
} else {
|
||||
/* handle application data */
|
||||
/* @todo: call recved for unencrypted overhead only */
|
||||
altcp_recved(conn->inner_conn, p->tot_len);
|
||||
return altcp_mbedtls_handle_rx_data(conn);
|
||||
return altcp_mbedtls_handle_rx_data(conn, state);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass queued decoded rx data to application */
|
||||
static err_t
|
||||
altcp_mbedtls_pass_rx_data(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
|
||||
{
|
||||
struct pbuf *buf;
|
||||
LWIP_ASSERT("conn != NULL", conn != NULL);
|
||||
LWIP_ASSERT("state != NULL", state != NULL);
|
||||
buf = state->rx_app;
|
||||
if (buf) {
|
||||
if (conn->recv) {
|
||||
u16_t tot_len = state->rx_app->tot_len;
|
||||
err_t err;
|
||||
/* this needs to be increased first because the 'recved' call may come nested */
|
||||
state->rx_passed_unrecved += tot_len;
|
||||
err = conn->recv(conn->arg, conn, state->rx_app, ERR_OK);
|
||||
if (err != ERR_OK) {
|
||||
/* not received, leave the pbuf(s) queued (and decrease 'unrecved' again) */
|
||||
state->rx_passed_unrecved -= tot_len;
|
||||
LWIP_ASSERT("state->rx_passed_unrecved >= 0", state->rx_passed_unrecved >= 0);
|
||||
if (state->rx_passed_unrecved < 0) {
|
||||
state->rx_passed_unrecved = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
pbuf_free(buf);
|
||||
}
|
||||
state->rx_app = NULL;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* Helper function that processes rx application data stored in rx pbuf chain */
|
||||
static err_t
|
||||
altcp_mbedtls_handle_rx_data(struct altcp_pcb *conn)
|
||||
altcp_mbedtls_handle_rx_data(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
|
||||
{
|
||||
int ret;
|
||||
altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
|
||||
if (!state) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
LWIP_ASSERT("state != NULL", state != NULL);
|
||||
if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
|
||||
/* handshake not done yet */
|
||||
return ERR_VAL;
|
||||
@ -311,19 +354,37 @@ altcp_mbedtls_handle_rx_data(struct altcp_pcb *conn)
|
||||
altcp_abort(conn);
|
||||
return ERR_ABRT;
|
||||
} else {
|
||||
LWIP_ASSERT("bogus receive length", ret <= 0xFFFF && ret <= PBUF_POOL_BUFSIZE);
|
||||
err_t err;
|
||||
LWIP_ASSERT("bogus receive length", ret <= PBUF_POOL_BUFSIZE);
|
||||
/* trim pool pbuf to actually decoded length */
|
||||
pbuf_realloc(buf, (uint16_t)ret);
|
||||
|
||||
if (conn->recv) {
|
||||
err_t err;
|
||||
state->rx_passed_unrecved += buf->tot_len;
|
||||
err = conn->recv(conn->arg, conn, buf, ERR_OK);
|
||||
if (err == ERR_ABRT) {
|
||||
return ERR_ABRT;
|
||||
state->bio_bytes_appl += ret;
|
||||
if (mbedtls_ssl_get_bytes_avail(&state->ssl_context) == 0) {
|
||||
/* Record is done, now we know the share between application and protocol bytes
|
||||
and can adjust the RX window by the protocol bytes.
|
||||
The rest is 'recved' by the application calling our 'recved' fn. */
|
||||
int overhead_bytes;
|
||||
LWIP_ASSERT("bogus byte counts", state->bio_bytes_read > state->bio_bytes_appl);
|
||||
overhead_bytes = state->bio_bytes_read - state->bio_bytes_appl;
|
||||
altcp_mbedtls_lower_recved(conn->inner_conn, overhead_bytes);
|
||||
state->bio_bytes_read = 0;
|
||||
state->bio_bytes_appl = 0;
|
||||
}
|
||||
|
||||
if (state->rx_app == NULL) {
|
||||
state->rx_app = buf;
|
||||
} else {
|
||||
pbuf_free(buf);
|
||||
pbuf_cat(state->rx_app, buf);
|
||||
}
|
||||
err = altcp_mbedtls_pass_rx_data(conn, state);
|
||||
if (err != ERR_OK) {
|
||||
if (err == ERR_ABRT) {
|
||||
/* recv callback needs to return this as the pcb is deallocated */
|
||||
return err;
|
||||
}
|
||||
/* we hide all other errors as we retry feeding the pbuf to the app later */
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -341,8 +402,7 @@ altcp_mbedtls_bio_recv(void *ctx, unsigned char *buf, size_t len)
|
||||
altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
|
||||
struct pbuf* p;
|
||||
u16_t ret;
|
||||
/* limit number of byts to copy to fit into an s16_t for pbuf_header */
|
||||
u16_t copy_len = (u16_t)LWIP_MIN(len, 0x7FFF);
|
||||
u16_t copy_len;
|
||||
err_t err;
|
||||
|
||||
if (state == NULL) {
|
||||
@ -350,46 +410,49 @@ altcp_mbedtls_bio_recv(void *ctx, unsigned char *buf, size_t len)
|
||||
}
|
||||
p = state->rx;
|
||||
|
||||
LWIP_ASSERT("len is too big", len <= 0xFFFF);
|
||||
|
||||
if (p == NULL) {
|
||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
}
|
||||
/* limit number of bytes to copy to fit into an s16_t for pbuf_header */
|
||||
copy_len = (u16_t)LWIP_MIN(len, 0x7FFF);
|
||||
/* limit number of bytes again to copy from first pbuf in a chain only */
|
||||
copy_len = LWIP_MIN(copy_len, p->len);
|
||||
/* copy the data */
|
||||
ret = pbuf_copy_partial(p, buf, copy_len, 0);
|
||||
LWIP_ASSERT("ret <= p->len", ret <= p->len);
|
||||
LWIP_ASSERT("ret == copy_len", ret == copy_len);
|
||||
/* hide the copied bytes from the pbuf */
|
||||
err = pbuf_header(p, -(s16_t)ret);
|
||||
LWIP_ASSERT("error", err == ERR_OK);
|
||||
if(p->len == 0) {
|
||||
if (p->len == 0) {
|
||||
/* the first pbuf has been fully read, free it */
|
||||
state->rx = p->next;
|
||||
p->next = NULL;
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
state->bio_bytes_read += (int)ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Sent callback from lower connection (i.e. TCP)
|
||||
* @todo: Pass on the correct number of bytes to the application.
|
||||
* This is somewhat tricky as we don't know the data/overhead ratio...
|
||||
* This only informs the upper layer to try to send more, not about
|
||||
* the number of ACKed bytes.
|
||||
*/
|
||||
static err_t
|
||||
altcp_mbedtls_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
|
||||
{
|
||||
struct altcp_pcb *conn = (struct altcp_pcb *)arg;
|
||||
LWIP_UNUSED_ARG(len);
|
||||
if (conn) {
|
||||
u16_t sent_upper;
|
||||
altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
|
||||
LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
|
||||
if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
|
||||
/* @todo: do something here? */
|
||||
return ERR_OK;
|
||||
}
|
||||
/* @todo: this is not accurate yet, need to fix byte counting to upper and lower conn */
|
||||
sent_upper = (u16_t)LWIP_MIN(len, state->tx_unacked);
|
||||
state->tx_unacked -= sent_upper;
|
||||
if (conn->sent && sent_upper) {
|
||||
return conn->sent(conn->arg, conn, len);
|
||||
/* call upper sent with len==0 if the application already sent data */
|
||||
if ((state->flags & ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT) && conn->sent) {
|
||||
return conn->sent(conn->arg, conn, 0);
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
@ -406,7 +469,9 @@ altcp_mbedtls_lower_poll(void *arg, struct altcp_pcb *inner_conn)
|
||||
if (conn) {
|
||||
LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
|
||||
/* check if there's unreceived rx data */
|
||||
altcp_mbedtls_handle_rx_data(conn);
|
||||
if (conn->state) {
|
||||
altcp_mbedtls_handle_rx_data(conn, (altcp_mbedtls_state_t *)conn->state);
|
||||
}
|
||||
if (conn->poll) {
|
||||
return conn->poll(conn->arg, conn);
|
||||
}
|
||||
@ -624,6 +689,7 @@ altcp_mbedtls_set_poll(struct altcp_pcb *conn, u8_t interval)
|
||||
static void
|
||||
altcp_mbedtls_recved(struct altcp_pcb *conn, u16_t len)
|
||||
{
|
||||
u16_t lower_recved;
|
||||
altcp_mbedtls_state_t *state;
|
||||
if (conn == NULL) {
|
||||
return;
|
||||
@ -635,11 +701,15 @@ altcp_mbedtls_recved(struct altcp_pcb *conn, u16_t len)
|
||||
if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
|
||||
return;
|
||||
}
|
||||
LWIP_ASSERT("recved mismatch", state->rx_passed_unrecved >= len);
|
||||
state->rx_passed_unrecved -= len;
|
||||
lower_recved = len;
|
||||
if (lower_recved > state->rx_passed_unrecved) {
|
||||
LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("bogus recved count (len > state->rx_passed_unrecved / %d / %d)",
|
||||
len, state->rx_passed_unrecved));
|
||||
lower_recved = (u16_t)state->rx_passed_unrecved;
|
||||
}
|
||||
state->rx_passed_unrecved -= lower_recved;
|
||||
|
||||
/* to pass this down, we need to convert 'altcp_recved' handling in lower_recv first
|
||||
altcp_recved(conn->inner_conn, len);*/
|
||||
altcp_recved(conn->inner_conn, lower_recved);
|
||||
}
|
||||
|
||||
static err_t
|
||||
@ -741,8 +811,10 @@ altcp_mbedtls_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t
|
||||
}
|
||||
|
||||
ret = mbedtls_ssl_write(&state->ssl_context, (const unsigned char *)dataptr, len);
|
||||
/* try to send data... */
|
||||
altcp_output(conn->inner_conn);
|
||||
if(ret == len) {
|
||||
state->tx_unacked += len;
|
||||
state->flags |= ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT;
|
||||
return ERR_OK;
|
||||
} else if (ret <= 0) {
|
||||
/* @todo: convert error to err_t */
|
||||
|
@ -53,19 +53,22 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#define ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE 0x01
|
||||
#define ALTCP_MBEDTLS_FLAGS_RX_CLOSED 0x02
|
||||
#define ALTCP_MBEDTLS_FLAGS_TX_CLOSED 0x04
|
||||
#define ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT 0x02
|
||||
#define ALTCP_MBEDTLS_FLAGS_RX_CLOSED 0x04
|
||||
#define ALTCP_MBEDTLS_FLAGS_TX_CLOSED 0x08
|
||||
#define ALTCP_MBEDTLS_FLAGS_CLOSED (ALTCP_MBEDTLS_FLAGS_RX_CLOSED|ALTCP_MBEDTLS_FLAGS_TX_CLOSED)
|
||||
#define ALTCP_MBEDTLS_FLAGS_UPPER_CALLED 0x08
|
||||
#define ALTCP_MBEDTLS_FLAGS_UPPER_CALLED 0x10
|
||||
|
||||
typedef struct altcp_mbedtls_state_s {
|
||||
void *conf;
|
||||
mbedtls_ssl_context ssl_context;
|
||||
/* chain of rx pbufs (before decryption) */
|
||||
struct pbuf* rx;
|
||||
struct pbuf* rx_app;
|
||||
u8_t flags;
|
||||
size_t rx_passed_unrecved;
|
||||
size_t tx_unacked;
|
||||
int rx_passed_unrecved;
|
||||
int bio_bytes_read;
|
||||
int bio_bytes_appl;
|
||||
} altcp_mbedtls_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
Loading…
Reference in New Issue
Block a user