diff --git a/api/net/sf/briar/api/protocol/ProtocolConstants.java b/api/net/sf/briar/api/protocol/ProtocolConstants.java
index ba87ec12cfe6d3713c0e4f4e60ce3c0715e1a61f..aa54918a36135178c7253eadb283830a7c3f5dc3 100644
--- a/api/net/sf/briar/api/protocol/ProtocolConstants.java
+++ b/api/net/sf/briar/api/protocol/ProtocolConstants.java
@@ -1,19 +1,18 @@
 package net.sf.briar.api.protocol;
 
-import net.sf.briar.api.transport.TransportConstants;
+import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH;
 
 public interface ProtocolConstants {
 
 	/**
 	 * The maximum length of a serialised packet in bytes. To allow for future
 	 * changes in the protocol, this is smaller than the minimum connection
-	 * length minus the encryption and authentication overhead.
+	 * length minus the maximum encryption and authentication overhead.
 	 */
-	static final int MAX_PACKET_LENGTH =
-		TransportConstants.MIN_CONNECTION_LENGTH - 1024;
+	static final int MAX_PACKET_LENGTH = MIN_CONNECTION_LENGTH / 2;
 
 	/** The maximum number of transports a node may support. */
-	static final int MAX_TRANSPORTS = 50;
+	static final int MAX_TRANSPORTS = 25;
 
 	/** The maximum number of properties per transport. */
 	static final int MAX_PROPERTIES_PER_TRANSPORT = 100;
diff --git a/components/net/sf/briar/transport/ConnectionWriterImpl.java b/components/net/sf/briar/transport/ConnectionWriterImpl.java
index 39ee053790185b1ac20944024e0f7e1395e19cbc..4932cff5c748dcb459d0272a3293207afa4b31a0 100644
--- a/components/net/sf/briar/transport/ConnectionWriterImpl.java
+++ b/components/net/sf/briar/transport/ConnectionWriterImpl.java
@@ -35,11 +35,7 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 	}
 
 	public long getRemainingCapacity() {
-		long capacity = out.getRemainingCapacity();
-		int maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH;
-		long frames = (long) Math.ceil((double) capacity / maxPayloadLength);
-		long overhead = (frames + 1) * (HEADER_LENGTH + MAC_LENGTH);
-		return capacity - overhead - length;
+		return out.getRemainingCapacity();
 	}
 
 	@Override
@@ -83,12 +79,9 @@ class ConnectionWriterImpl extends OutputStream implements ConnectionWriter {
 		length += len;
 	}
 
-	private void writeFrame(boolean lastFrame) throws IOException {
+	private void writeFrame(boolean finalFrame) throws IOException {
 		if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
-		int capacity = (int) Math.min(frameLength, out.getRemainingCapacity());
-		int paddingLength = capacity - HEADER_LENGTH - length - MAC_LENGTH;
-		if(paddingLength < 0) throw new IllegalStateException();
-		out.writeFrame(frame, length, lastFrame ? 0 : paddingLength, lastFrame);
+		out.writeFrame(frame, length, finalFrame);
 		length = 0;
 		frameNumber++;
 	}
diff --git a/components/net/sf/briar/transport/FrameWriter.java b/components/net/sf/briar/transport/FrameWriter.java
index c3167b411d2d0807b46ace94c8c66753e0bc1268..947e8bf1b70d08ff4f3ede7860286b6ba1c0b3ea 100644
--- a/components/net/sf/briar/transport/FrameWriter.java
+++ b/components/net/sf/briar/transport/FrameWriter.java
@@ -5,8 +5,8 @@ import java.io.IOException;
 interface FrameWriter {
 
 	/** Writes the given frame. */
-	void writeFrame(byte[] frame, int payloadLength, int paddingLength,
-			boolean lastFrame) throws IOException;
+	void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
+			throws IOException;
 
 	/** Flushes the stack. */
 	void flush() throws IOException;
diff --git a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
index c9080a6c5e95ec010abc51c9ed00ad83b92a843c..9d6a67260cf6a0737c77598b3db88b802f95bec2 100644
--- a/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
+++ b/components/net/sf/briar/transport/OutgoingEncryptionLayer.java
@@ -6,6 +6,7 @@ import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH;
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
+import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -33,7 +34,7 @@ class OutgoingEncryptionLayer implements FrameWriter {
 			AuthenticatedCipher frameCipher, ErasableKey tagKey,
 			ErasableKey frameKey, int frameLength) {
 		this.out = out;
-		this.capacity = capacity - TAG_LENGTH;
+		this.capacity = capacity;
 		this.tagCipher = tagCipher;
 		this.frameCipher = frameCipher;
 		this.tagKey = tagKey;
@@ -64,17 +65,11 @@ class OutgoingEncryptionLayer implements FrameWriter {
 		writeTag = false;
 	}
 
-	public void writeFrame(byte[] frame, int payloadLength, int paddingLength,
-			boolean lastFrame) throws IOException {
-		int plaintextLength = HEADER_LENGTH + payloadLength + paddingLength;
-		int ciphertextLength = plaintextLength + MAC_LENGTH;
-		if(ciphertextLength > frameLength)
-			throw new IllegalArgumentException();
-		if(!lastFrame && ciphertextLength < frameLength)
-			throw new IllegalArgumentException();
+	public void writeFrame(byte[] frame, int payloadLength, boolean finalFrame)
+			throws IOException {
 		// If the initiator's side of the connection is closed without writing
-		// any payload or padding, don't write a tag or an empty frame
-		if(writeTag && lastFrame && payloadLength + paddingLength == 0) return;
+		// any data, don't write anything to the underlying transport
+		if(writeTag && finalFrame && payloadLength == 0) return;
 		// Write the tag if required
 		if(writeTag) {
 			TagEncoder.encodeTag(ciphertext, tagCipher, tagKey);
@@ -85,10 +80,20 @@ class OutgoingEncryptionLayer implements FrameWriter {
 				tagKey.erase();
 				throw e;
 			}
+			capacity -= TAG_LENGTH;
 			writeTag = false;
 		}
 		// Encode the header
-		FrameEncoder.encodeHeader(frame, lastFrame, payloadLength);
+		FrameEncoder.encodeHeader(frame, finalFrame, payloadLength);
+		// Don't pad the final frame
+		int plaintextLength, ciphertextLength;
+		if(finalFrame) {
+			plaintextLength = HEADER_LENGTH + payloadLength;
+			ciphertextLength = plaintextLength + MAC_LENGTH;
+		} else {
+			plaintextLength = frameLength - MAC_LENGTH;
+			ciphertextLength = frameLength;
+		}
 		// If there's any padding it must all be zeroes
 		for(int i = HEADER_LENGTH + payloadLength; i < plaintextLength; i++)
 			frame[i] = 0;
@@ -120,6 +125,10 @@ class OutgoingEncryptionLayer implements FrameWriter {
 	}
 
 	public long getRemainingCapacity() {
-		return capacity;
+		long capacityExcludingTag = writeTag ? capacity - TAG_LENGTH : capacity;
+		long frames = capacityExcludingTag / frameLength;
+		long frameNumbers = MAX_32_BIT_UNSIGNED - frameNumber + 1;
+		int maxPayloadLength = frameLength - HEADER_LENGTH - MAC_LENGTH;
+		return maxPayloadLength * Math.min(frames, frameNumbers);
 	}
 }
\ No newline at end of file
diff --git a/test/net/sf/briar/transport/ConnectionWriterTest.java b/test/net/sf/briar/transport/ConnectionWriterTest.java
index 9b7ebc082d641dd4c008ad0b34cb1d3b29bb5d84..6c416658ece44c33c6f26a165c0298381600d20a 100644
--- a/test/net/sf/briar/transport/ConnectionWriterTest.java
+++ b/test/net/sf/briar/transport/ConnectionWriterTest.java
@@ -48,14 +48,14 @@ public class ConnectionWriterTest extends BriarTestCase {
 				MIN_CONNECTION_LENGTH, secret, true);
 		// Check that the connection writer thinks there's room for a packet
 		long capacity = w.getRemainingCapacity();
-		assertTrue(capacity >= MAX_PACKET_LENGTH);
-		assertTrue(capacity <= MIN_CONNECTION_LENGTH);
+		assertTrue(capacity > MAX_PACKET_LENGTH);
+		assertTrue(capacity < MIN_CONNECTION_LENGTH);
 		// Check that there really is room for a packet
 		byte[] payload = new byte[MAX_PACKET_LENGTH];
 		w.getOutputStream().write(payload);
 		w.getOutputStream().close();
 		long used = out.size();
-		assertTrue(used >= MAX_PACKET_LENGTH);
+		assertTrue(used > MAX_PACKET_LENGTH);
 		assertTrue(used <= MIN_CONNECTION_LENGTH);
 	}
 
@@ -67,14 +67,14 @@ public class ConnectionWriterTest extends BriarTestCase {
 				MIN_CONNECTION_LENGTH, secret, false);
 		// Check that the connection writer thinks there's room for a packet
 		long capacity = w.getRemainingCapacity();
-		assertTrue(capacity >= MAX_PACKET_LENGTH);
-		assertTrue(capacity <= MIN_CONNECTION_LENGTH);
+		assertTrue(capacity > MAX_PACKET_LENGTH);
+		assertTrue(capacity < MIN_CONNECTION_LENGTH);
 		// Check that there really is room for a packet
 		byte[] payload = new byte[MAX_PACKET_LENGTH];
 		w.getOutputStream().write(payload);
 		w.getOutputStream().close();
 		long used = out.size();
-		assertTrue(used >= MAX_PACKET_LENGTH);
+		assertTrue(used > MAX_PACKET_LENGTH);
 		assertTrue(used <= MIN_CONNECTION_LENGTH);
 	}
 }