From 450bbeb8a00cc695cc2c62f48c821388d6191e00 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 5 Jun 2012 00:44:05 +0200 Subject: Add support for receiving reordered packets --- src/config.c | 3 +++ src/fastd.h | 7 ++++-- src/method_null.c | 2 +- src/method_xsalsa20_poly1305.c | 49 ++++++++++++++++++++++++++++++++---------- src/protocol_ec25519_fhmqvc.c | 6 +++--- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/src/config.c b/src/config.c index f826c18..015ff8f 100644 --- a/src/config.c +++ b/src/config.c @@ -60,6 +60,9 @@ static void default_config(fastd_config *conf) { conf->peer_stale_time = 90; conf->eth_addr_stale_time = 300; + conf->reorder_count = 32; + conf->reorder_time = 10; + conf->ifname = NULL; memset(&conf->bind_addr_in, 0, sizeof(struct sockaddr_in)); diff --git a/src/fastd.h b/src/fastd.h index abf8c07..8d7ce59 100644 --- a/src/fastd.h +++ b/src/fastd.h @@ -92,8 +92,8 @@ struct _fastd_method { bool (*session_want_refresh)(fastd_context *ctx, fastd_method_session_state *session); void (*session_free)(fastd_context *ctx, fastd_method_session_state *session); - bool (*encrypt)(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in); - bool (*decrypt)(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in); + bool (*encrypt)(fastd_context *ctx, fastd_peer *peer, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in); + bool (*decrypt)(fastd_context *ctx, fastd_peer *peer, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in); }; union _fastd_peer_address { @@ -133,6 +133,9 @@ struct _fastd_config { unsigned peer_stale_time; unsigned eth_addr_stale_time; + unsigned reorder_count; + unsigned reorder_time; + char *ifname; struct sockaddr_in bind_addr_in; diff --git a/src/method_null.c b/src/method_null.c index fbdf1f5..e8b3fca 100644 --- a/src/method_null.c +++ b/src/method_null.c @@ -57,7 +57,7 @@ static bool method_session_want_refresh(fastd_context *ctx, fastd_method_session static void method_session_free(fastd_context *ctx, fastd_method_session_state *session) { } -static bool method_passthrough(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { +static bool method_passthrough(fastd_context *ctx, fastd_peer *peer, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { *out = in; return true; } diff --git a/src/method_xsalsa20_poly1305.c b/src/method_xsalsa20_poly1305.c index 9331265..95fbfa2 100644 --- a/src/method_xsalsa20_poly1305.c +++ b/src/method_xsalsa20_poly1305.c @@ -39,6 +39,9 @@ struct _fastd_method_session_state { uint8_t send_nonce[NONCEBYTES]; uint8_t receive_nonce[NONCEBYTES]; + + struct timespec receive_last; + uint64_t receive_reorder_seen; }; @@ -55,19 +58,20 @@ static inline void increment_nonce(uint8_t nonce[NONCEBYTES]) { } } -static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES]) { +static inline bool is_nonce_valid(const uint8_t nonce[NONCEBYTES], const uint8_t old_nonce[NONCEBYTES], int64_t *age) { if ((nonce[0] & 1) != (old_nonce[0] & 1)) return false; int i; + *age = 0; + for (i = NONCEBYTES-1; i >= 0; i--) { - if (nonce[i] > old_nonce[i]) - return true; - if (nonce[i] < old_nonce[i]) - return false; + *age *= 256; + *age += old_nonce[i]-nonce[i]; } - return false; + *age /= 2; + return true; } static size_t method_max_packet_size(fastd_context *ctx) { @@ -128,7 +132,7 @@ static void method_session_free(fastd_context *ctx, fastd_method_session_state * } } -static bool method_encrypt(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { +static bool method_encrypt(fastd_context *ctx, fastd_peer *peer, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { fastd_buffer_pull_head(&in, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); @@ -150,7 +154,7 @@ static bool method_encrypt(fastd_context *ctx, fastd_method_session_state *sessi return true; } -static bool method_decrypt(fastd_context *ctx, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { +static bool method_decrypt(fastd_context *ctx, fastd_peer *peer, fastd_method_session_state *session, fastd_buffer *out, fastd_buffer in) { if (in.len < NONCEBYTES) return false; @@ -161,8 +165,17 @@ static bool method_decrypt(fastd_context *ctx, fastd_method_session_state *sessi memcpy(nonce, in.data, NONCEBYTES); memset(nonce+NONCEBYTES, 0, crypto_secretbox_xsalsa20poly1305_NONCEBYTES-NONCEBYTES); - if (!is_nonce_valid(nonce, session->receive_nonce)) + int64_t age; + if (!is_nonce_valid(nonce, session->receive_nonce, &age)) return false; + + if (age >= 0) { + if (timespec_diff(&ctx->now, &session->receive_last) > ctx->conf->reorder_time*1000) + return false; + + if (age > ctx->conf->reorder_count) + return false; + } fastd_buffer_pull_head(&in, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES-NONCEBYTES); memset(in.data, 0, crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES); @@ -180,9 +193,23 @@ static bool method_decrypt(fastd_context *ctx, fastd_method_session_state *sessi fastd_buffer_free(in); - fastd_buffer_push_head(out, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); + if (age < 0) { + session->receive_reorder_seen >>= age; + session->receive_reorder_seen |= (1 >> (age+1)); + memcpy(session->receive_nonce, nonce, NONCEBYTES); + session->receive_last = ctx->now; + } + else if (age == 0 || session->receive_reorder_seen & (1 << (age-1))) { + pr_debug(ctx, "dropping duplicate packet from %P (age %u)", peer, (unsigned)age); + fastd_buffer_free(*out); + *out = fastd_buffer_alloc(crypto_secretbox_xsalsa20poly1305_ZEROBYTES, 0, 0); + } + else { + pr_debug(ctx, "accepting reordered packet from %P (age %u)", peer, (unsigned)age); + session->receive_reorder_seen |= (1 << (age-1)); + } - memcpy(session->receive_nonce, nonce, NONCEBYTES); + fastd_buffer_push_head(out, crypto_secretbox_xsalsa20poly1305_ZEROBYTES); return true; } diff --git a/src/protocol_ec25519_fhmqvc.c b/src/protocol_ec25519_fhmqvc.c index 31cf2b6..64d941c 100644 --- a/src/protocol_ec25519_fhmqvc.c +++ b/src/protocol_ec25519_fhmqvc.c @@ -637,12 +637,12 @@ static void protocol_handle_recv(fastd_context *ctx, fastd_peer *peer, fastd_buf bool ok = false; if (is_session_valid(ctx, &peer->protocol_state->old_session)) { - if (ctx->conf->method->decrypt(ctx, peer->protocol_state->old_session.method_state, &recv_buffer, buffer)) + if (ctx->conf->method->decrypt(ctx, peer, peer->protocol_state->old_session.method_state, &recv_buffer, buffer)) ok = true; } if (!ok) { - if (ctx->conf->method->decrypt(ctx, peer->protocol_state->session.method_state, &recv_buffer, buffer)) { + if (ctx->conf->method->decrypt(ctx, peer, peer->protocol_state->session.method_state, &recv_buffer, buffer)) { ok = true; if (peer->protocol_state->old_session.method_state) { @@ -698,7 +698,7 @@ static void protocol_send(fastd_context *ctx, fastd_peer *peer, fastd_buffer buf } fastd_buffer send_buffer; - if (!ctx->conf->method->encrypt(ctx, session->method_state, &send_buffer, buffer)) + if (!ctx->conf->method->encrypt(ctx, peer, session->method_state, &send_buffer, buffer)) goto fail; fastd_send(ctx, &peer->address, send_buffer); -- cgit v1.2.3