From 8aef6ce29278acab610d21de35d7483cdce623df Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 17 Jul 2022 11:28:00 -0400 Subject: [PATCH] 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 --- history.txt | 15 +++++++++++++ .../src/net/i2p/router/RouterVersion.java | 2 +- .../transport/udp/InboundEstablishState.java | 3 ++- .../transport/udp/InboundEstablishState2.java | 6 +++++ .../udp/OutboundEstablishState2.java | 22 ++++++++++++++----- .../router/transport/udp/PacketBuilder2.java | 10 +++++++-- .../i2p/router/transport/udp/PeerState.java | 2 +- .../i2p/router/transport/udp/PeerState2.java | 11 ++++++---- .../i2p/router/transport/udp/SSU2Payload.java | 7 +++++- .../i2p/router/transport/udp/UDPAddress.java | 5 +++++ 10 files changed, 68 insertions(+), 15 deletions(-) diff --git a/history.txt b/history.txt index 02d5c483c..882bb1342 100644 --- a/history.txt +++ b/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 diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 3c8f141dc..470d52b4b 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -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 = ""; diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java index 0a5172dbf..90bc7fe77 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java @@ -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(); diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java index d075c7b64..cfe3914aa 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState2.java @@ -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"); diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java index 3381b2c80..d77aafe6d 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState2.java @@ -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"); diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java index 64a7a5de4..68ccf5b41 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder2.java @@ -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(fragments); - peer.fragmentsSent(pktNum, fragments); + peer.fragmentsSent(pktNum, length, fragments); } return packet; } diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java index 2a818bde0..3cf66cbf8 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java @@ -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 diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState2.java b/router/java/src/net/i2p/router/transport/udp/PeerState2.java index 47c747063..530504695 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState2.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState2.java @@ -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 fragments) { + void fragmentsSent(long pktNum, int length, List fragments) { List 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); } } diff --git a/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java b/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java index bc5183124..50348817c 100644 --- a/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java +++ b/router/java/src/net/i2p/router/transport/udp/SSU2Payload.java @@ -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; diff --git a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java index cf8c8e9db..892cd629c 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPAddress.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPAddress.java @@ -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;