SSU2: Fixes part 6

MTU and related fixes:
Fix max fragment size
Fix max space for acks
Fix max packet size
Pull MTU from best address in Session Confirmed
Pass MTU from establish state to peer state
Use SSU2 min/default/max MTU in PeerState
Stop looping when out of space in OMF (SSU 1 too)
This commit is contained in:
zzz
2022-03-09 13:53:16 -05:00
parent 25af51faf9
commit 97736cef1c
8 changed files with 125 additions and 65 deletions

View File

@@ -954,7 +954,7 @@ class EstablishmentManager {
}
}
}
}
} // else IES2 sets PS2 MTU
// 0 is the default
//peer.setTheyRelayToUsAs(0);
@@ -1060,13 +1060,14 @@ class EstablishmentManager {
peer.setCurrentCipherKey(state.getCipherKey());
peer.setCurrentMACKey(state.getMACKey());
peer.setTheyRelayToUsAs(state.getReceivedRelayTag());
int mtu = state.getRemoteAddress().getMTU();
if (mtu > 0)
peer.setHisMTU(mtu);
} else {
OutboundEstablishState2 state2 = (OutboundEstablishState2) state;
// OES2 sets PS2 MTU
peer = state2.getPeerState();
}
int mtu = state.getRemoteAddress().getMTU();
if (mtu > 0)
peer.setHisMTU(mtu);
// 0 is the default
//peer.setWeRelayToThemAs(0);

View File

@@ -25,6 +25,7 @@ import net.i2p.data.router.RouterAddress;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.RouterContext;
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
import net.i2p.router.transport.TransportImpl;
import static net.i2p.router.transport.udp.SSU2Util.*;
import net.i2p.util.Addresses;
import net.i2p.util.Log;
@@ -53,6 +54,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
private long _timeReceived;
// not adjusted for RTT
private long _skew;
private int _mtu;
private PeerState2 _pstate;
// testing
@@ -197,15 +199,32 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
// TODO ban
throw new DataFormatException("SSU2 network ID mismatch");
}
// try to find the right address, because we need the MTU
boolean isIPv6 = _aliceIP.length == 16;
List<RouterAddress> addrs = _transport.getTargetAddresses(ri);
RouterAddress ra = null;
for (RouterAddress addr : addrs) {
// skip NTCP w/o "s"
// skip SSU 1 address w/o "s"
if (addrs.size() > 1 && addr.getTransportStyle().equals("SSU") && addr.getOption("s") == null)
continue;
String host = addr.getHost();
if (host == null)
host = "";
String caps = addr.getOption(UDPAddress.PROP_CAPACITY);
if (caps == null)
caps = "";
if (isIPv6) {
if (!host.contains(":") && !caps.contains(TransportImpl.CAP_IPV6))
continue;
} else {
if (!host.contains(".") && !caps.contains(TransportImpl.CAP_IPV4))
continue;
}
ra = addr;
break;
}
if (ra == null)
throw new DataFormatException("no SSU2 addr");
String siv = ra.getOption("i");
@@ -227,6 +246,33 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
if (!"2".equals(ra.getOption("v")))
throw new DataFormatException("bad SSU2 v");
String smtu = ra.getOption(UDPAddress.PROP_MTU);
int mtu = 0;
try {
mtu = Integer.parseInt(smtu);
} catch (NumberFormatException nfe) {}
if (mtu == 0) {
if (ra.getTransportStyle().equals(UDPTransport.STYLE2)) {
mtu = PeerState2.DEFAULT_MTU;
} else {
if (isIPv6)
mtu = PeerState2.DEFAULT_SSU_IPV6_MTU;
else
mtu = PeerState2.DEFAULT_SSU_IPV4_MTU;
}
} else {
// TODO if too small, give up now
if (ra.getTransportStyle().equals(UDPTransport.STYLE2)) {
mtu = Math.min(Math.max(mtu, PeerState2.MIN_MTU), PeerState2.MAX_MTU);
} else {
if (isIPv6)
mtu = Math.min(Math.max(mtu, PeerState2.MIN_SSU_IPV6_MTU), PeerState2.MAX_SSU_IPV6_MTU);
else
mtu = Math.min(Math.max(mtu, PeerState2.MIN_SSU_IPV4_MTU), PeerState2.MAX_SSU_IPV4_MTU);
}
}
_mtu = mtu;
Hash h = _receivedUnconfirmedIdentity.calculateHash();
try {
RouterInfo old = _context.netDb().store(h, ri);
@@ -539,6 +585,7 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
_sendHeaderEncryptKey1, h_ba, h_ab);
// PS2.super adds CLOCK_SKEW_FUDGE that doesn't apply here
_pstate.adjustClockSkew(_skew - (_rtt / 2) - PeerState.CLOCK_SKEW_FUDGE);
_pstate.setHisMTU(_mtu);
}
/**

View File

@@ -84,7 +84,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
// We need the MTU so the Session Confirmed can fit the RI in
int mtu = addr.getMTU();
if (mtu == 0) {
if (ra.getTransportStyle().equals("SSU2")) {
if (ra.getTransportStyle().equals(UDPTransport.STYLE2)) {
mtu = PeerState2.DEFAULT_MTU;
} else {
if (_bobIP.length == 16)
@@ -94,7 +94,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
}
} else {
// TODO if too small, give up now
if (ra.getTransportStyle().equals("SSU2")) {
if (ra.getTransportStyle().equals(UDPTransport.STYLE2)) {
mtu = Math.min(Math.max(mtu, PeerState2.MIN_MTU), PeerState2.MAX_MTU);
} else {
if (_bobIP.length == 16)
@@ -497,6 +497,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
_pstate.confirmedPacketsSent(_sessConfForReTX);
// PS2.super adds CLOCK_SKEW_FUDGE that doesn't apply here
_pstate.adjustClockSkew(_skew - (_rtt / 2) - PeerState.CLOCK_SKEW_FUDGE);
_pstate.setHisMTU(_mtu);
}
confirmedPacketsSent();
return _pstate;

View File

@@ -464,24 +464,30 @@ class OutboundMessageFragments {
maxAvail = PacketBuilder.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
else
maxAvail = PacketBuilder2.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
for (int j = i + 1; j < toSend.size(); j++) {
next = toSend.get(j);
int nextDataSize = next.state.fragmentSize(next.num);
//if (PacketBuilder.canFitAnotherFragment(peer, sendNext.size(), curTotalDataSize, nextDataSize)) {
//if (_builder.canFitAnotherFragment(peer, sendNext.size(), curTotalDataSize, nextDataSize)) {
if (nextDataSize <= maxAvail) {
// add it
toSend.remove(j);
j--;
sendNext.add(next);
curTotalDataSize += nextDataSize;
if (peer.getVersion() == 1)
maxAvail = PacketBuilder.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
else
maxAvail = PacketBuilder2.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
if (_log.shouldLog(Log.INFO))
_log.info("Adding in additional " + next + " to " + peer);
} // else too big
// if less than 16, just use it for acks, don't even try to look for a tiny fragment
if (maxAvail >= 16) {
for (int j = i + 1; j < toSend.size(); j++) {
next = toSend.get(j);
int nextDataSize = next.state.fragmentSize(next.num);
//if (PacketBuilder.canFitAnotherFragment(peer, sendNext.size(), curTotalDataSize, nextDataSize)) {
//if (_builder.canFitAnotherFragment(peer, sendNext.size(), curTotalDataSize, nextDataSize)) {
if (nextDataSize <= maxAvail) {
// add it
toSend.remove(j);
j--;
sendNext.add(next);
curTotalDataSize += nextDataSize;
if (peer.getVersion() == 1)
maxAvail = PacketBuilder.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
else
maxAvail = PacketBuilder2.getMaxAdditionalFragmentSize(peer, sendNext.size(), curTotalDataSize);
if (_log.shouldLog(Log.INFO))
_log.info("Adding in additional " + next + " to " + peer);
// if less than 16, just use it for acks, don't even try to look for a tiny fragment
if (maxAvail < 16)
break;
} // else too big
}
}
}

View File

@@ -63,11 +63,11 @@ class PacketBuilder2 {
/** Same for IPv4 and IPv6 */
public static final int UDP_HEADER_SIZE = PacketBuilder.UDP_HEADER_SIZE;
/** 74 */
/** 60 */
public static final int MIN_DATA_PACKET_OVERHEAD = IP_HEADER_SIZE + UDP_HEADER_SIZE + DATA_HEADER_SIZE + MAC_LEN;
public static final int IPV6_HEADER_SIZE = PacketBuilder.IPV6_HEADER_SIZE;
/** 94 */
/** 80 */
public static final int MIN_IPV6_DATA_PACKET_OVERHEAD = IPV6_HEADER_SIZE + UDP_HEADER_SIZE + DATA_HEADER_SIZE + MAC_LEN;
/// FIXME
@@ -99,11 +99,12 @@ class PacketBuilder2 {
/**
* Will a packet to 'peer' that already has 'numFragments' fragments
* totalling 'curDataSize' bytes fit another fragment of size 'newFragSize' ??
* totalling 'curDataSize' bytes fit another fragment?
*
* This doesn't leave anything for acks.
* This doesn't leave anything for acks or anything else.
*
* @param numFragments &gt;= 1
* @return max additional fragment size
*/
public static int getMaxAdditionalFragmentSize(PeerState peer, int numFragments, int curDataSize) {
int available = peer.getMTU() - curDataSize;
@@ -111,7 +112,7 @@ class PacketBuilder2 {
available -= MIN_IPV6_DATA_PACKET_OVERHEAD;
else
available -= MIN_DATA_PACKET_OVERHEAD;
// OVERHEAD above includes 1 * FRAGMENT+HEADER_SIZE;
// OVERHEAD above includes 1 * FRAGMENT_HEADER_SIZE;
// this adds for the others, plus the new one.
available -= numFragments * FIRST_FRAGMENT_HEADER_SIZE;
return available;
@@ -167,13 +168,19 @@ class PacketBuilder2 {
// calculate size available for acks
int currentMTU = peer.getMTU();
int availableForAcks = currentMTU - dataSize;
// includes UDP header
int ipHeaderSize;
if (peer.isIPv6()) {
availableForAcks -= MIN_IPV6_DATA_PACKET_OVERHEAD;
ipHeaderSize = IPV6_HEADER_SIZE;
ipHeaderSize = IPV6_HEADER_SIZE + UDP_HEADER_SIZE;
} else {
availableForAcks -= MIN_DATA_PACKET_OVERHEAD;
ipHeaderSize = IP_HEADER_SIZE;
ipHeaderSize = IP_HEADER_SIZE + UDP_HEADER_SIZE;
}
if (otherBlocks != null) {
for (Block block : otherBlocks) {
availableForAcks -= block.getTotalLength();
}
}
// make the packet
@@ -189,6 +196,7 @@ class PacketBuilder2 {
if (otherBlocks != null)
bcnt += otherBlocks.size();
List<Block> blocks = new ArrayList<Block>(bcnt);
// payload only
int sizeWritten = 0;
// add the acks
@@ -203,6 +211,8 @@ class PacketBuilder2 {
if (_log.shouldDebug())
_log.debug("Sending acks " + block + " to " + peer);
}
} else if (_log.shouldDebug()) {
_log.debug("No room for acks, MTU: " + currentMTU + " data: " + dataSize + " available: " + availableForAcks);
}
// now write each fragment
@@ -238,15 +248,14 @@ class PacketBuilder2 {
// DateTime block every so often, if room
if ((pktNum & (DATETIME_SEND_FREQUENCY - 1)) == DATETIME_SEND_FREQUENCY - 1 &&
sizeWritten + ipHeaderSize + 7 <= currentMTU) {
ipHeaderSize + SHORT_HEADER_SIZE + sizeWritten + 7 + MAC_LEN <= currentMTU) {
Block block = new SSU2Payload.DateTimeBlock(_context);
blocks.add(block);
off += 7;
sizeWritten += 7;
}
// FIXME
Block block = getPadding(sizeWritten, currentMTU);
Block block = getPadding(ipHeaderSize + SHORT_HEADER_SIZE + sizeWritten + MAC_LEN, currentMTU);
if (block != null) {
blocks.add(block);
int sz = block.getTotalLength();
@@ -268,11 +277,12 @@ class PacketBuilder2 {
// compare to LARGE_MTU
// Also happens on switch between IPv4 and IPv6
if (_log.shouldWarn()) {
int maxMTU = peer.isIPv6() ? PeerState.MAX_IPV6_MTU : PeerState.LARGE_MTU;
if (off + (ipHeaderSize + UDP_HEADER_SIZE) > maxMTU) {
int maxMTU = PeerState2.MAX_MTU;
off += MAC_LEN;
if (off + ipHeaderSize > maxMTU) {
_log.warn("Size is " + off + " for " + packet +
" data size " + dataSize +
" pkt size " + (off + (ipHeaderSize + UDP_HEADER_SIZE)) +
" pkt size " + (off + ipHeaderSize) +
" MTU " + currentMTU +
" Fragments: " + DataHelper.toString(fragments), new Exception());
}
@@ -1003,7 +1013,7 @@ class PacketBuilder2 {
}
/**
* @param len current length of the packet
* @param len current length of the packet including IP/UDP header
* @param max max length of the packet
* @return null if no room
*/

View File

@@ -398,20 +398,15 @@ public class PeerState {
_receivePeriodBegin = now;
_remoteIP = addr.getAddress().getAddress();
_remotePort = addr.getPort();
_mtu = PeerState2.MIN_MTU;
_mtuReceive = PeerState2.MIN_MTU;
if (_remoteIP.length == 4) {
_mtu = DEFAULT_MTU;
_mtuReceive = DEFAULT_MTU;
_largeMTU = transport.getMTU(false);
} else {
_mtu = MIN_IPV6_MTU;
_mtuReceive = MIN_IPV6_MTU;
_largeMTU = transport.getMTU(true);
}
// RFC 5681 sec. 3.1
if (_mtu > 1095)
_sendWindowBytes = 3 * _mtu;
else
_sendWindowBytes = 4 * _mtu;
_sendWindowBytes = 3 * _mtu;
_sendWindowBytesRemaining = _sendWindowBytes;
_lastACKSend = -1;
@@ -859,7 +854,7 @@ public class PeerState {
// window and SST set in highestSeqNumAcked()
bwe = -1; // for log below
} else {
_sendWindowBytes = isIPv6() ? MAX_IPV6_MTU : LARGE_MTU;
_sendWindowBytes = getVersion() == 2 ? PeerState2.MAX_MTU : (isIPv6() ? MAX_IPV6_MTU : LARGE_MTU);
bwe = _bwEstimator.getBandwidthEstimate();
_slowStartThreshold = Math.max( (int)(bwe * _rtt), 2 * _mtu);
}
@@ -1230,12 +1225,12 @@ public class PeerState {
_context.statManager().addRateData("udp.mtuIncrease", _mtuIncreases);
}
} else if (!wantLarge && _mtu == _largeMTU) {
_mtu = _remoteIP.length == 4 ? MIN_MTU : MIN_IPV6_MTU;
_mtu = getVersion() == 2 ? PeerState2.MIN_MTU : (_remoteIP.length == 4 ? MIN_MTU : MIN_IPV6_MTU);
_mtuDecreases++;
_context.statManager().addRateData("udp.mtuDecrease", _mtuDecreases);
}
} else {
_mtu = _remoteIP.length == 4 ? DEFAULT_MTU : MIN_IPV6_MTU;
_mtu = getVersion() == 2 ? PeerState2.MIN_MTU : (_remoteIP.length == 4 ? MIN_MTU : MIN_IPV6_MTU);
}
}
@@ -1244,7 +1239,8 @@ public class PeerState {
*/
synchronized void setHisMTU(int mtu) {
if (mtu <= MIN_MTU || mtu >= _largeMTU ||
(_remoteIP.length == 16 && mtu <= MIN_IPV6_MTU))
(_remoteIP.length == 16 && mtu <= MIN_IPV6_MTU) ||
(getVersion() == 2 && mtu <= PeerState2.MIN_MTU))
return;
_largeMTU = mtu;
if (mtu < _mtu)
@@ -1310,10 +1306,10 @@ public class PeerState {
int minMTU;
if (_remoteIP.length == 4) {
size += OVERHEAD_SIZE;
minMTU = MIN_MTU;
minMTU = getVersion() == 2 ? PeerState2.MIN_MTU : MIN_MTU;
} else {
size += IPV6_OVERHEAD_SIZE;
minMTU = MIN_IPV6_MTU;
minMTU = getVersion() == 2 ? PeerState2.MIN_MTU : MIN_IPV6_MTU;
}
if (size <= minMTU) {
_consecutiveSmall++;

View File

@@ -116,8 +116,9 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
/**
* how much payload data can we shove in there?
* Does NOT leave any room for acks, we'll fit them in when we can.
* This is 5 bytes too low for first or only fragment.
* This is 5 bytes too low for first or only fragment,
* because the 9 byte I2NP header is included in that fragment.
* Does NOT leave any room for acks with a full-size fragment.
*
* @return MTU - 68 (IPv4), MTU - 88 (IPv6)
*/
@@ -127,13 +128,14 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
// 40 + 8 + 16 + 3 + 5 + 16 = 88 (IPv6)
return _mtu -
(_remoteIP.length == 4 ? PacketBuilder2.MIN_DATA_PACKET_OVERHEAD : PacketBuilder2.MIN_IPV6_DATA_PACKET_OVERHEAD) -
DATA_FOLLOWON_EXTRA_SIZE; // Followon fragment block overhead (5)
(SSU2Payload.BLOCK_HEADER_SIZE + DATA_FOLLOWON_EXTRA_SIZE);
}
/**
* Packet overhead
* Does NOT leave any room for acks, we'll fit them in when we can.
* This is 5 bytes too high for first or only fragment.
* This is 5 bytes too high for first or only fragment,
* because the 9 byte I2NP header is included in that fragment.
* Does NOT leave any room for acks with a full-size fragment.
*
* @return 68 (IPv4), 88 (IPv6)
*/
@@ -142,7 +144,7 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
// 20 + 8 + 16 + 3 + 5 + 16 = 68 (IPv4)
// 40 + 8 + 16 + 3 + 5 + 16 = 88 (IPv6)
return (_remoteIP.length == 4 ? PacketBuilder2.MIN_DATA_PACKET_OVERHEAD : PacketBuilder2.MIN_IPV6_DATA_PACKET_OVERHEAD) +
DATA_FOLLOWON_EXTRA_SIZE; // Followon fragment block overhead (5)
SSU2Payload.BLOCK_HEADER_SIZE + DATA_FOLLOWON_EXTRA_SIZE;
}
/**
@@ -300,7 +302,7 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
}
int payloadLen = len - (SHORT_HEADER_SIZE + MAC_LEN);
if (_log.shouldInfo())
_log.info("New pkt rcvd " + n + " on " + this);
_log.info("New " + len + " byte pkt " + n + " rcvd on " + this);
processPayload(data, off + SHORT_HEADER_SIZE, payloadLen);
packetReceived(payloadLen);
} catch (GeneralSecurityException gse) {

View File

@@ -55,9 +55,6 @@ final class SSU2Util {
public static final int MIN_HANDSHAKE_DATA_LEN = SESSION_HEADER_SIZE + TOTAL_PROT_SAMPLE_LEN;
/** 3 byte block header */
public static final int FULL_I2NP_HEADER_SIZE = SSU2Payload.BLOCK_HEADER_SIZE;
/** 3 byte block header */
public static final int FIRST_FRAGMENT_HEADER_SIZE = SSU2Payload.BLOCK_HEADER_SIZE;
@@ -69,8 +66,8 @@ final class SSU2Util {
/** 3 byte block header + 4 byte msg ID + 1 byte fragment info = 8 */
public static final int FOLLOWON_FRAGMENT_HEADER_SIZE = SSU2Payload.BLOCK_HEADER_SIZE + DATA_FOLLOWON_EXTRA_SIZE;
/** 16 byte short header + 3 = 19 */
public static final int DATA_HEADER_SIZE = SHORT_HEADER_SIZE + FULL_I2NP_HEADER_SIZE;
/** 16 byte short header */
public static final int DATA_HEADER_SIZE = SHORT_HEADER_SIZE;
/**
* The message types, 0-10, as bytes