forked from I2P_Developers/i2p.i2p
SSU2: MTU and other fixes
Use minimum MTU for IPv6 session confirmed to avoid PMTU issues Set default MTU for known IPv6 tunnel brokers Check for s mismatch in session confirmed RI Don't put DateTime block after Termination block Fix first message failed check Log sent data packet size Log tweaks
This commit is contained in:
15
history.txt
15
history.txt
@ -1,3 +1,18 @@
|
||||
2022-07-17 zzz
|
||||
* SSU2:
|
||||
- More MTU fixes
|
||||
- Verify static key in RI
|
||||
- Don't put DateTime block after Termination block
|
||||
- Fix first message fail check
|
||||
|
||||
2022-07-15 zzz
|
||||
* SSU2:
|
||||
- MTU fixes
|
||||
- Send retry with termination on clock skew
|
||||
|
||||
2022-07-12 zzz
|
||||
* SSU2: Fail session if first outbound message fails
|
||||
|
||||
2022-07-10 zzz
|
||||
* SSU2: Fix NACK handling
|
||||
|
||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Git";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 11;
|
||||
public final static long BUILD = 12;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@ -553,7 +553,8 @@ class InboundEstablishState {
|
||||
// buf.append(" SentY: ").append(Base64.encode(_sentY, 0, 4));
|
||||
//buf.append(" Bob: ").append(Addresses.toString(_bobIP, _bobPort));
|
||||
buf.append(" lifetime: ").append(DataHelper.formatDuration(getLifetime()));
|
||||
buf.append(" RelayTag: ").append(_sentRelayTag);
|
||||
if (_sentRelayTag > 0)
|
||||
buf.append(" RelayTag: ").append(_sentRelayTag);
|
||||
//buf.append(" SignedOn: ").append(_sentSignedOnTime);
|
||||
buf.append(' ').append(_currentState);
|
||||
return buf.toString();
|
||||
|
@ -259,6 +259,12 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
||||
throw new DataFormatException("bad SSU2 S");
|
||||
if (s.length != 32)
|
||||
throw new DataFormatException("bad SSU2 S len");
|
||||
byte[] nb = new byte[32];
|
||||
// compare to the _handshakeState
|
||||
_handshakeState.getRemotePublicKey().getPublicKey(nb, 0);
|
||||
if (!DataHelper.eqCT(s, 0, nb, 0, KEY_LEN))
|
||||
throw new DataFormatException("s mismatch in RI: " + ri);
|
||||
|
||||
if (!"2".equals(ra.getOption("v")))
|
||||
throw new DataFormatException("bad SSU2 v");
|
||||
|
||||
|
@ -53,7 +53,7 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
private byte[] _sendHeaderEncryptKey2;
|
||||
private byte[] _rcvHeaderEncryptKey2;
|
||||
private final byte[] _rcvRetryHeaderEncryptKey2;
|
||||
private int _mtu;
|
||||
private final int _mtu;
|
||||
private byte[] _sessReqForReTX;
|
||||
private byte[][] _sessConfForReTX;
|
||||
private long _timeReceived;
|
||||
@ -402,8 +402,18 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
public byte[] getRcvRetryHeaderEncryptKey2() { return _rcvRetryHeaderEncryptKey2; }
|
||||
public InetSocketAddress getSentAddress() { return _bobSocketAddress; }
|
||||
|
||||
/** what is the largest packet we can send to the peer? */
|
||||
public int getMTU() { return _mtu; }
|
||||
/**
|
||||
* What is the largest packet we can send to the peer?
|
||||
* Only used for Session Confirmed packets.
|
||||
* Session Request is very small.
|
||||
*/
|
||||
public int getMTU() {
|
||||
// To avoid PMTU problems on brokered IPv6 tunnels, make it the minimum.
|
||||
// Data phase will probe and increase if possible
|
||||
if (_bobIP == null || _bobIP.length == 16)
|
||||
return PeerState2.MIN_MTU;
|
||||
return _mtu;
|
||||
}
|
||||
|
||||
public synchronized void receiveRetry(UDPPacket packet) throws GeneralSecurityException {
|
||||
////// TODO state check
|
||||
@ -447,11 +457,11 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
return;
|
||||
}
|
||||
if (_timeReceived == 0)
|
||||
throw new GeneralSecurityException("No DateTime block in Session/Token Request");
|
||||
throw new GeneralSecurityException("No DateTime block in Retry");
|
||||
// _nextSend is now(), from packetReceived()
|
||||
_skew = _nextSend - _timeReceived;
|
||||
if (_skew > MAX_SKEW || _skew < 0 - MAX_SKEW)
|
||||
throw new GeneralSecurityException("Skew exceeded in Session/Token Request: " + _skew);
|
||||
throw new GeneralSecurityException("Skew exceeded in Retry: " + _skew);
|
||||
createNewState(_routerAddress);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Received a retry on " + this);
|
||||
@ -577,6 +587,8 @@ class OutboundEstablishState2 extends OutboundEstablishState implements SSU2Payl
|
||||
byte[] save = new byte[len];
|
||||
System.arraycopy(data, off, save, 0, len);
|
||||
_sessConfForReTX[i] = save;
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Sess conf pkt " + i + '/' + packets.length + " bytes: " + len);
|
||||
}
|
||||
if (_rcvHeaderEncryptKey2 == null)
|
||||
_rcvHeaderEncryptKey2 = SSU2Util.hkdf(_context, _handshakeState.getChainingKey(), "SessCreateHeader");
|
||||
|
@ -239,17 +239,22 @@ class PacketBuilder2 {
|
||||
}
|
||||
|
||||
// now the other blocks, if any
|
||||
boolean hasTermination = false;
|
||||
if (otherBlocks != null) {
|
||||
for (Block block : otherBlocks) {
|
||||
blocks.add(block);
|
||||
int sz = block.getTotalLength();
|
||||
off += sz;
|
||||
sizeWritten += sz;
|
||||
if (block.getType() == SSU2Payload.BLOCK_TERMINATION)
|
||||
hasTermination = true;
|
||||
}
|
||||
}
|
||||
|
||||
// DateTime block every so often, if room
|
||||
if ((pktNum & (DATETIME_SEND_FREQUENCY - 1)) == DATETIME_SEND_FREQUENCY - 1 &&
|
||||
// not allowed after termination
|
||||
if (!hasTermination &&
|
||||
(pktNum & (DATETIME_SEND_FREQUENCY - 1)) == DATETIME_SEND_FREQUENCY - 1 &&
|
||||
ipHeaderSize + SHORT_HEADER_SIZE + sizeWritten + 7 + MAC_LEN <= currentMTU) {
|
||||
Block block = new SSU2Payload.DateTimeBlock(_context);
|
||||
blocks.add(block);
|
||||
@ -266,6 +271,7 @@ class PacketBuilder2 {
|
||||
}
|
||||
SSU2Payload.writePayload(data, SHORT_HEADER_SIZE, blocks);
|
||||
pkt.setLength(off);
|
||||
int length = off + ipHeaderSize;
|
||||
//if (_log.shouldDebug())
|
||||
// _log.debug("Packet " + pktNum + " before encryption:\n" + HexDump.dump(data, 0, off));
|
||||
|
||||
@ -302,7 +308,7 @@ class PacketBuilder2 {
|
||||
fragments = Collections.singletonList(fragments.get(0));
|
||||
else
|
||||
fragments = new ArrayList<Fragment>(fragments);
|
||||
peer.fragmentsSent(pktNum, fragments);
|
||||
peer.fragmentsSent(pktNum, length, fragments);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
@ -1559,7 +1559,7 @@ public class PeerState {
|
||||
_transport.failed(state);
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Message expired: " + state + " to: " + this);
|
||||
if (!_isInbound && msg.getSeqNum() == 0)
|
||||
if (!_isInbound && state.getSeqNum() == 0)
|
||||
totalFail = true; // see below
|
||||
} else {
|
||||
// it can not have an OutNetMessage if the source is the
|
||||
|
@ -355,10 +355,10 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
|
||||
packetReceived(payloadLen);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad encrypted packet:\n" + HexDump.dump(data, off, len), gse);
|
||||
_log.warn("Bad encrypted packet on: " + this + '\n' + HexDump.dump(data, off, len), gse);
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Bad encrypted packet:\n" + HexDump.dump(data, off, len), ioobe);
|
||||
_log.warn("Bad encrypted packet on: " + this + '\n' + HexDump.dump(data, off, len), ioobe);
|
||||
}
|
||||
}
|
||||
|
||||
@ -658,8 +658,11 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
|
||||
/**
|
||||
* Record the mapping of packet number to what fragments were in it,
|
||||
* so we can process acks.
|
||||
*
|
||||
* @param length including ip/udp header, for logging only
|
||||
*
|
||||
*/
|
||||
void fragmentsSent(long pktNum, List<PacketBuilder.Fragment> fragments) {
|
||||
void fragmentsSent(long pktNum, int length, List<PacketBuilder.Fragment> fragments) {
|
||||
List<PacketBuilder.Fragment> old = _sentMessages.putIfAbsent(Long.valueOf(pktNum), fragments);
|
||||
if (old != null) {
|
||||
// shouldn't happen
|
||||
@ -667,7 +670,7 @@ public class PeerState2 extends PeerState implements SSU2Payload.PayloadCallback
|
||||
_log.warn("Dup send of pkt " + pktNum + " on " + this);
|
||||
} else {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("New data pkt " + pktNum + " sent with " + fragments.size() + " fragments on " + this);
|
||||
_log.debug("New " + length + " byte data pkt " + pktNum + " sent with " + fragments.size() + " fragments on " + this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ class SSU2Payload {
|
||||
private static final int BLOCK_I2NP = 3;
|
||||
private static final int BLOCK_FIRSTFRAG = 4;
|
||||
private static final int BLOCK_FOLLOWONFRAG = 5;
|
||||
private static final int BLOCK_TERMINATION = 6;
|
||||
public static final int BLOCK_TERMINATION = 6;
|
||||
private static final int BLOCK_RELAYREQ = 7;
|
||||
private static final int BLOCK_RELAYRESP = 8;
|
||||
private static final int BLOCK_RELAYINTRO = 9;
|
||||
@ -393,6 +393,11 @@ class SSU2Payload {
|
||||
type = ttype;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.55
|
||||
*/
|
||||
public int getType() { return type; }
|
||||
|
||||
/** @return new offset */
|
||||
public int write(byte[] tgt, int off) {
|
||||
tgt[off++] = (byte) type;
|
||||
|
@ -119,6 +119,11 @@ class UDPAddress {
|
||||
} else {
|
||||
cmtu = MTU.rectify(isIPv6, imtu);
|
||||
}
|
||||
} else if (_host != null) {
|
||||
if (_host.startsWith("2001:470:"))
|
||||
cmtu = 1472;
|
||||
else if (_host.startsWith("2a06:a004:"))
|
||||
cmtu = 1420;
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
_mtu = cmtu;
|
||||
|
Reference in New Issue
Block a user