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:
zzz
2022-07-17 11:28:00 -04:00
parent 097fa34e91
commit 8aef6ce292
10 changed files with 68 additions and 15 deletions

View File

@ -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

View File

@ -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 = "";

View File

@ -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();

View File

@ -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");

View File

@ -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");

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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;