From 0209f12d27d29f3aedc09b228bd289305851c75d Mon Sep 17 00:00:00 2001
From: Guus Sliepen <guus@tinc-vpn.org>
Date: Sat, 10 Jan 2015 23:00:51 +0100
Subject: [PATCH] Correctly estimate the initial MTU for legacy packets.

---
 src/cipher.h         |  1 +
 src/net_packet.c     | 19 +++++++++++++++++++
 src/openssl/cipher.c |  7 +++++++
 3 files changed, 27 insertions(+)

diff --git a/src/cipher.h b/src/cipher.h
index f194c0dc..3f98c18f 100644
--- a/src/cipher.h
+++ b/src/cipher.h
@@ -33,6 +33,7 @@ extern cipher_t *cipher_open_by_nid(int) __attribute__ ((__malloc__));
 extern cipher_t *cipher_open_blowfish_ofb(void) __attribute__ ((__malloc__));
 extern void cipher_close(cipher_t *);
 extern size_t cipher_keylength(const cipher_t *);
+extern size_t cipher_blocksize(const cipher_t *);
 extern void cipher_get_key(const cipher_t *, void *);
 extern bool cipher_set_key(cipher_t *, void *, bool) __attribute__ ((__warn_unused_result__));
 extern bool cipher_set_key_from_rsa(cipher_t *, void *, size_t, bool) __attribute__ ((__warn_unused_result__));
diff --git a/src/net_packet.c b/src/net_packet.c
index 37535c17..8bf399f1 100644
--- a/src/net_packet.c
+++ b/src/net_packet.c
@@ -929,6 +929,25 @@ static length_t choose_initial_maxmtu(node_t *n) {
 		mtu -= SPTPS_DATAGRAM_OVERHEAD;
 		if((n->options >> 24) >= 4)
 			mtu -= sizeof(node_id_t) + sizeof(node_id_t);
+	} else {
+		mtu -= digest_length(n->outdigest);
+
+		/* Now it's tricky. We use CBC mode, so the length of the
+		   encrypted payload must be a multiple of the blocksize. The
+		   sequence number is also part of the encrypted payload, so we
+		   must account for it after correcting for the blocksize.
+		   Furthermore, the padding in the last block must be at least
+		   1 byte. */
+
+		length_t blocksize = cipher_blocksize(n->outcipher);
+
+		if(blocksize > 1) {
+			mtu /= blocksize;
+			mtu *= blocksize;
+			mtu--;
+		}
+
+		mtu -= 4; // seqno
 	}
 
 	if (mtu < 512) {
diff --git a/src/openssl/cipher.c b/src/openssl/cipher.c
index 9b39a288..04aee27e 100644
--- a/src/openssl/cipher.c
+++ b/src/openssl/cipher.c
@@ -79,6 +79,13 @@ size_t cipher_keylength(const cipher_t *cipher) {
 	return cipher->cipher->key_len + cipher->cipher->iv_len;
 }
 
+size_t cipher_blocksize(const cipher_t *cipher) {
+	if(!cipher || !cipher->cipher)
+		return 1;
+
+	return cipher->cipher->block_size;
+}
+
 bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
 	bool result;