Evan has disappeared so I have had to review zidel's branch. Here is my summary 
and thoughts on what needs to be fixed, what needs to be done, etc. Hopefully 
Google will let me fill in the evaluation form for zidel and thus zidel 
continue with this work; this is good stuff which will make a significant 
difference once completed and debugged.

zidel/packetFormat branch reviewed up to 
bc67c3e4370ef584c58166cc4f6d326030dc83f2

MAJOR REMAINING CHALLENGES AFAICS:
- How to make message ID re-use safe, or how to not re-use message ID's and 
still not use unreasonable numbers of bytes.
- Memory DoS protection. May be related to the old debate about sender 
initiated vs recipient initiated.
- Seriously consider only allowing 2 bytes for length for messages; if allow 3, 
set an arbitrary maximum.
- Acceptable length of an HMAC.
- Implement an IV based on CTR mode (i.e. encrypt the packet number) using a 
separate key derived during key setup (we will need several: one for each 
direction, one for the IV, and one for the HMAC - we will need to use a proper 
HMAC; JFK gives a very simple procedure for deriving them, hash the generated 
key with a constant).
- CPU DoS: Predict the next N encrypted seqno's and watch for them.
- Block transfers - eventually, after we don't need to worry too much about 
back compatibility; translating from one form of block transfer to another 
isn't worth the effort.
- Bulk transfers and maximum message size - Eventually we will want to 
integrate bulk transfers (which can be huge, and don't want to keep everything 
in RAM); for now, they will work fine as-is, although inefficiently.
- Appropriate window size for message counters. IMHO 8192 may not be enough for 
high bandwidth moderate latency, at least not in the medium term.
- Appropriate window size for packet numbers.


- New interface PacketFormat. Includes maybeSendPacket() and 
handleReceivedPacket(). Will implement everything related to the packet format.
- New class FNPWrapper: Move mustSendNotificationNow(), maybeSendPacket(). 
Implements old FNP.
- Separate IncomingPacketFilterImpl from FNPPacketMangler, very simple class to 
lookup address and feed through PeerNode to FNPPacketMangler.
- New FNP format is implemented through NewPacketFormat.
- NewPacketFormat supports message fragmentation. This is *the* big change in 
the new format. It allows dramatic improvements in many areas: In particular, 
we can send small packets, we need very little padding, we can deal with any 
MTU, future transports with small packets are much more feasible.
- Messages can be either small (0-255 bytes) or big (0-16MB). Currently these 
are both initiated by the sender, allowing for many fun DoS possibilities. THIS 
WILL NEED FUTURE WORK.
- We still ack individual packets. Which automatically acks all the ranges 
included in the packet.
- Use first 4 bytes of SHA256 as HMAC. ********* TOO SHORT ???? Maybe should be 
variable depending on packet size? RESEARCH THIS!
- PCFB encrypt the hash, then the packet itself. *********** NO IV! This is 
*BAD*! IV should be generated in CTR mode by encrypting the sequence number 
with a session key (other than the main crypto session key). We already ensure 
that sequence numbers don't wrap around for regular FNP. Also we should change 
the API so it's impossible to use PCFB mode with no IV, since it's suicidal.
- We still use one 4 byte ack and then 1 byte deltas. ****** We should compress 
each ack relative to the preceding ack, not relative to the first ack (and sort 
them, of course) ****** Also, we should provide an escape mechanism to deal 
with acks which are out of range i.e. to send a full 4 bytes where necessary; 
this sort of arbitrary limit can cause chaos in error handling later. I suggest 
overriding the top bit in the byte count to indicate there is another set of 
acks. *******
- Add PeerMessageQueue.grabQueuedMessageItem() to get a single MessageItem.
- Message ID allocation can fail, in which case we just send in-flight 
messages. ******* This will result in busy-looping, does the 
don't-try-till-can-send mechanism we use in old FNP work with new FNP?
- It is not yet clear how many packets can be in flight???? Looks like the 256 
limit will be lifted? **************
- We allow up to 8192 messages in flight. We use the top few bits for flags. We 
recycle message ID's. ********* Is it safe to recycle message ID's - even in 
the presence of replays and delayed packets?
- If we receive a message and don't have the length yet (because we don't have 
the first packet), we buffer the data anyway. This is essential to avoid 
unnecessary resends, but it does allow for memory DoS, especially the way it's 
been implemented. ************
- Add SparseBitmap class to contain range tracking logic. Similar code was in 
ReceivedPacketNumbers and temporarily in MessageWrapper.
- Fully integrated, receive and send code is wired in.
- We allocate a sequence number for ack-only packets, but don't expect or send 
an ack. ***** Is this a good idea? The old code uses -1, since we want to 
conserve sequence numbers especially if we end up running into the window 
limit? otoh but we may want to use the sequence number for crypto; if so we 
could just have a parallel sequence for ack only?
- TODO: ******* Key DoS protection: Predict the encrypted sequence numbers and 
watch for them. Ignore everything else. Don't try to decrypt if the sequence 
number isn't in the expected window.
- TODO: ******* Memory DoS protection. This was discussed extensively, the 
strategies are fairly simple.
- TODO: ******* Block transfers. Obviously we should deal with this *after* the 
rest is deployed; we can use the existing code for some time.
- TODO: ******* Bulk transfers and reconsider size limits. IMHO anything over 
64K should be a bulk transfer (which doesn't have to be buffered in RAM), we 
will need to keep the bulk transfer data code for quite some time anyway to 
deal with UOM. Long term it'd be good to not have to fragment it in fixed 
inappropriate sized blocks though. In any case I suspect 2 bytes would be 
enough for length for any message that we are happy to buffer in RAM? If we 
don't do this we need a maximum of less than 16MB!
- Various classes e.g. NPFPacket etc, splitting the logic up into manageable 
sized chunks.
- Indenting.
- Add some unit tests, including both low level and high level.
- Resending logic: When we send a packet, we resend any packets which have been 
sent more than 2 round-trips ago and haven't been acked yet. ****** Resending 
requires allocating a packet number. This can fail if the window of in flight 
packets is too big. The current workaround for FNP in general (dunno if it 
applies to new FNP but it looks like it should) is to not send anything until 
we get acks... I don't think that applies to resends and acks, but here it 
would????
- ********** IIRC TCP resends immediately if it sees a later sequence number? 
This reduces latency but will occasionally result in unnecessary resends? I 
think what TCP does is this: If a packet hasn't been acked after 4 RTT's (you 
can get this from PeerNode.fourRTTs()), we resend it; and if we get an ack for 
a packet, we assume those before it haven't arrived and resend them. I may be 
remembering wrong, there's something about 1.5 RTTs somewhere? Look it up, 
maybe on the wiki page? What the old FNP code does is hideous, of course.
- Only call onSent() once for any message.
- Lots of sanity checks.
- We try the old and new keys just like old FNP.
- Messages are sent by priority. ******** This interacts with anti-memory-DoS; 
we have to tradeoff between sending high priority stuff and completing existing 
sends.
- Message seqno's are reused. ********* We need to seriously consider this, I'm 
not sure it's safe, even with a packet-level window. How can it be made safe? 
Safe = we get a somewhat delayed packet, it contains a chunk from message 1, we 
can be confident that wasn't from the previous message 1.
- ********** Is a hard limit of 8192 messages in flight acceptable medium term? 
It may become an obstacle.
- Hacks to ignore fragments of a given message segnum on incoming packets for a 
while after it was used. Which reduce performance, and still ack the packet, so 
probably cause data loss. *******


44012f14126e2364fb57ce0a4f879eccfb332ce9
- *4 byte* HMAC?????? You need more than this, certainly on larger packets, or 
you need higher level authentication somehow.
- Need a better scheme for ack's - the current code is limited to a window of 
256 packets but this is definitely not sustainable long term.

52715da7efc832a8c6a1767f771b6459bd909cd4
- Did we decide that we should only include the message length on the first 
packet? IIRC there were complicated discussions related to sender initiated vs 
recipient initiated...

11567fbd96c11f387f415717a7ef376e6c397273
- We may want to support including more than one fragment from the same message 
for jumbo frames or for fragmented retransmission where we've lost something in 
the middle. But we should probably round-robin, so we need to re-run the add 
started messages loop until there is no more to add or we finish.

aea9a7a63af7ff779398c09afaf353401df07ece
- Consider and document policy re which fragments to send first. It may be best 
to send the earliest fragment first? Since there are no message level hashes, 
the recipient may be able to process a partial message, in which case earliest 
fragment first makes sense.

866a2aa9ee74434d6ad4328dc8c5edaa75b779f6
- synchronize when allocating a sequence number!!

fee4eeb53c0d3c953e50fb9cca1c0491c37798ac
- SHA256 is not an HMAC because it's not keyed. If we encrypt it afterwards 
that sort of serves the same function but it would be better just to use a real 
HMAC.
- 4 bytes is not enough, certainly not for larger messages. We can't hash the 
messages either - except maybe the big ones. How to deal with this is a big 
open challenge - higher level hashes will be troublesome one way or another...

d70deac8cd005bc4b1af3b89d585bf51b28adc66
- Not sure there's any point, you could store them as ints and cast them to 
longs, or just require them to be positive? If you are going to use longs and 
restrict their range you probably need some asserts.

59e7088ca13761253eb1729b9e4c9f73af6ae205
- Here and elsewhere sign extension on bytes can trip you up. You need to do & 
0xff on the byte values, or use one of our provided methods for decoding, or 
mask the whole thing afterwards e.g. value = value | 0xffffff.

78aced73420599f544cf6aefca188ca9963b28d4
- assertTrue(s.contains(3, 2));
- this should throw!

f32ee7587ce8a3fa80574b35505aa66a080ef084
+                               //TODO: If the other side resends a packet 
after we have gotten all the data, we will
+                               //never ack the resent packet
- The standard solution to this is to ack resent packets. If we have processed 
a packet and we get a resend for it, ack it.

cd7d374b7823906a46a46a417fdface43bbe188a
- I'm not sure this is wise - if we use a full HMAC on big packets, that might 
kick the encrypted sequence number into the second block, thus becoming 
unpredictable? OTOH if we use an HMAC of the encrypted data (keyed from a key 
other than the encryption key), and don't encrypt it itself, it won't be a 
problem.

0a92c3f7a14d1f44f1f25013d045fbbeb2ff867b
- Can isFragmented() be bogus here with border cases? It is calculated based on 
the maximum header length - if it returns false and then we discover that with 
the actual header length we don't need to fragment, what happens?
- If we ever send packets from the same queue on separate threads, we will want 
to sync on the MessageWrapper during insertFragment.

349ba4b57cad858541039f5bcfe140019edbfb9c
- You may want to try setting the config node.testingDropPacketsEvery to 
simulate random packet loss.

5b3d6b32a0a7c1fb593df3c915d16a29a3d12790
- Do you need to copy the data for MessageFragment?

17096df5098e53881db1a6957c41b6c83e0ad4ec
- If frag == null, do we lose the message from the queue here?

5c537e76f15e8ef9e4ed974e5a41a31fd720f3b1
- Doesn't item itself have a method to call all the acks? it should...

bc1eb2fa33c0b2063992df3fb9b207c8c7b66f0c
- The unit test doesn't test what it says. The bytes should each be different 
to be sure about this.

1f893a30d456d04eca59e8eeca209c94d9769df4
- ********* We should try the previous key as well as unverified key.

7a316c720fcaed368d7ad35f8a1262aa4f91dce7
- it shouldn't be added if it's bad

43471bf6d7e59295c6a2e5a992eba49838830ff2
- acked ranges can be lost too, can't they? at least with some buffering/DoS 
protection schemes? I guess that would be a different function / a parameter; 
it's not the normal case, it would only happen if they explicitly tell us.

4a7586992a743c55a9ebcdb15567a746fd0f6314
- if you are confident that the packet has been processed already, you should 
ack it. if you're not you should process it. no??? otoh the fact that we resend 
at the level of messages means that it's not critical ... so maybe just dumping 
it is the right answer?
- we can use ranges to track efficiently which packets we have received, but 
there must be a limit... beyond which we might want an explicit nak?
- how does the window for acks relate to the window for setting up packet 
numbers?
- how does this interact with reusing message sequence numbers? if we have lots 
of small messages then it is possible to burn through 8192 messages in less 
than 128 packets (especially if half of those packet seqno's were allocated for 
ack-only packets i.e. are pointless)

b58aa213ed98f814eaab2c9893d1b12d4578c04e
- remove the sentPacket when returning due to no key???

a1acd68b51cc124a11ed1d496c82f82ce279da3a
- Don't count the RTT slots that haven't been filled in!

8f561a3ce585170c55a678e0cc0d62b6ab639a25
- Use entrySet() and Map.Entry<>.

899528d44d43b42d2ca8a304d9ecfe4847a5c50b
    Use maximum not average of last 10 RTTs
- Is this a good idea?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 197 bytes
Desc: This is a digitally signed message part.
URL: 
<https://emu.freenetproject.org/pipermail/devl/attachments/20100717/36569c5a/attachment.pgp>

Reply via email to