Change:
13.4.2.2. <del> An endpoint that sets ECT(0) or ECT(1) codepoints on packets it transmits MUST use the following steps on receiving an ACK frame to validate ECN. If this ACK frame newly acknowledges a packet that the endpoint sent with either ECT(0) or ECT(1) codepoints set, and if no ECN feedback is present in the ACK frame, validation fails.  This step protects against both a network element that zeroes out ECN bits and a peer that is unable to access ECN markings, since the peer could respond without ECN feedback in these cases. For validation to succeed, the total increase in ECT(0), ECT(1), and CE counts MUST be no smaller than the total number of QUIC packets sent with an ECT codepoint that are newly acknowledged in this ACK frame.  This step detects any network remarking from ECT(0), ECT(1), or CE codepoints to Not-ECT. Any increase in either ECT(0) or ECT(1) counts, plus any increase in the CE count, MUST be no smaller than the number of packets sent with the corresponding ECT codepoint that are newly acknowledged in this ACK frame.  This step detects any erroneous network remarking from ECT(0) to ECT(1) (or vice versa). Processing ECN counts out of order can result in validation failure. An endpoint SHOULD NOT perform this validation if this ACK frame does not advance the largest packet number acknowledged in this connection. </del> <ins> Erroneous application of ECN marks in the network can result in degraded connection performance.  An endpoint that receives an ACK frame with ECN counts therefore validates the counts before using them.  It performs this validation by comparing newly received counts against those from the last successfully processed ACK frame.  Any increase in ECN counts is validated based on the markings that were applied to packets that are newly acknowledged in the ACK frame. If an ACK frame newly acknowledges a packet that the endpoint sent with either ECT(0) or ECT(1) codepoints set, ECN validation fails if ECN counts are not present in the ACK frame.  This check detects a network element that zeroes out ECN bits or a peer that is unable to access ECN markings. ECN validation fails if the sum of the increase in ECT(0) and ECN-CE counts is less than the number of newly acknowledged packets that were originally sent with an ECT(0) marking.  Similarly, ECN validation fails if the sum of the increases to ECT(1) and ECN-CE counts is less than the number of newly acknowledged packets sent with an ECT(1) marking.  These checks can detect removal of ECN markings in the network. </ins> An endpoint could miss acknowledgements for a packet when ACK frames are lost.  It is therefore possible for the total increase in ECT(0), <del> ECT(1), and CE counts to be greater than the number of packets acknowledged in an ACK frame.  When this happens, and if validation succeeds, the local reference counts MUST be increased to match the counts in the ACK frame. </del> <ins> ECT(1), and ECN-CE counts to be greater than the number of packets acknowledged in an ACK frame.  This is why counts are permitted to be larger than might be accounted for by newly acknowledged packets. ECN validation MAY fail if the total count for an ECT(0) or ECT(1) marking exceeds the total number of packets sent with the corresponding marking.  In particular, an endpoint that never applies a particular marking can fail validation when a non-zero count for the corresponding marking is received.  This check can detect when packets are marked ECT(0) or ECT(1) in the network. Processing ECN counts out of order can result in validation failure. An endpoint SHOULD skip ECN validation on an ACK frame that does not increase the largest acknowledged packet number. </ins> 13.4.2.3.

Discussion:
This avoids "no smaller than", unqualified "total increase", and other such ambiguities. However, in doing this, I realized that I might interpret the original language as allowing a network to ADD markings. That seems wrong. But it's a non-editorial change. This version has that change. I'll make another PR with the hole left in place in case I'm wrong.
OK, I realize now my mistake. The draft allows for an increase because of the possibility of ACK loss. Counts might increase more than there are newly acknowledged packets because an endpoint might not receive an ACK for every packet that is accounted for in the increased count. I think that's worth a note. Though now I'm concerned about the third clause, which doesn't include any protection against this problem. I'll open an issue for that.
Sorry to ask for another review, but I had a few misfires yesterday (apparently). I think that this structure is much better. I have removed the check that uses the sum of ECT0+ECT1+CE. Any check on ECT0+CE or ECT1+CE is more precise anyway. The sum is only able to detect double-counting of CE-marked packets, but as the check is an inequality, we would allow what to happen. (That was what motivated my proposed additional check, but I agree that is not worth documenting.)
QUIC doesn't guarantee that every received packet will be acknowledged. At the same time, ECN validation uses counts of every received packet. The net effect of this is captured in this statement: -- URL For example, if a sender sends 10 ECT-marked packets and all of them arrive, they might only receive acknowledgments for 8 of these. The receiver will still report an increase of 10 for the corresponding markings. The sender compensates for this error by allowing this deviation and using the new reference number for future packets. Note that validation also depends on the receiver counting but not acknowledging a packet, then later changing its mind and acknowledging the packet. We don't state this, but we shouldn't have to. But that allowance only applies to the validation performed on the sum of ECT(0), ECT(1) and ECN-CE counts.

The validation for cross-marking of ECT(0) to ECT(1) or vice versa does not include this same protection. Thus, failure to acknowledge a packet will probably result in ECN validation failing. There are a few ways we might fix this. We could just allow for an increase here too. The net effect though is that we don't get to watch for cross-marking. We don't get any protection against cross marking like this. In other words, we can remove the check. It might be better to limit the allowed increase by the total excess in the total. That is, if then the check is and . The combined check becomes . That is, increases to ECT(X) + CE above what was newly acknowledged has to be represented in the total unaccounted value. If all unaccounted packets were CE marked, then they will be unaccounted for twice in the individual counts. If none of the unaccounted packets were CE marked, then they will be counted just once. That suggests the following text for the third check: This third check is now more precise than the second check, so I would suggest removing the combined check that we have current as step 2. A new combined check might detect remarking between ECT(0) and ECT(1): And finally, because most stacks only apply one type of marking:
I'm not convinced this needs fixing. To your point above: It is an exceedingly rare corner case, since this requires an ACK for a packet to not be sent or received, and then for there to be cross-marking within that fraction of connections. And in this case, ECN validation fails and is turned off. We knew corner cases existed, but we also had the escape hatch of turning off ECN when things were not "quite right". Why is that answer not good enough any more? (Or am I missing something?)
The problem is that we fixed this in one place, but not another. And we explicitly call out the need for the fix. The goal of validation was - by my understanding - finding obvious mistakes. But this creates false positives. If we want to do something minimal, then I would suggest that we simplify. That is, reduce the checks to or .

That's how I read the third validation check: but I agree it's not very clear. I think the intent was to do the minimal version you suggest above (which is why it's "either" instead of "both" in the text). I would be happy with us clarifying the text to say that more clearly.
Before this gets stuck. I'm abandoning this.
So, I am finally implementing this, and have questions. Section 13.4.2.2 says: What is meant by "total increase" here? I guess it is a delta, but against what? Against the values in the last received ACK frame? Against the "local reference counts" (which aren't really described well either, see below)? Or something else? Also, I guess the "total increase" compared here is a sum of these individual deltas? Again, "increase" relative to what? Same question about "total increase". And what are the "local reference counts", and how are they maintained?
You are supposed to keep three counters for the number of each ECT(0) and ECT(1) and CE marks reported so far on this connection (for this ack space). Increase means the difference of the count reported in the latest ACK compared to the current local counter value. Total increase is increase of ECT(0)+increase of ECT(1)+increase if CE compared to local current count of ECT(0)+ECT(1)+CE. Does that help?
It does, thanks. It would probably be useful to introduce those local reference counts before the bullet list, describe how they are maintained, and then use them when describing how to validate.
Yes. Will try to propose a PR. Thanks for implementing!
Actually, this doesn't make sense to me: The increase is the value in the current ACK frame minus the current local reference value. A sum of those increases is always going to be less than the sum of the total reference values?
Yes, sorry, in the text you don't compare the total increase to the local value of course. You compare the total increase to the number of packets acknowledged in an ACK frame
and then you also compare the increase of ECT(0)+increase in CE to number of packet sent with ECT(0) from the newly acknowledge packets, and same for ETC(1).

So you need to remember which packet was sent with which ECN codepoint if not all packets are send with the same codepoint.
So let me tell you what my code is doing now to verify incoming ACK-ECNs, and then you tell me if this is correct: I maintain ECN reference counter vectors () per packet number space. These hold the last counts received in an ACK-ECN from the peer. I count new ACKs for ECN-marked packets that I sent in a vector. (I only send ECNECT0 packets though, so this isn't really a vector, just a variable.) When I receive a new ACK-ECN with ECN counts (), I compute the vector as . I then do the verification per Section 13.4.2.2: (First bullet in 13.4.2.2 is easy, omitted here) Second bullet in 13.4.2.2: => verification failed Third bullet in 13.4.2.2: => verification failed. (Since I only send ECNECT0, I can omit looking at ECNECT1, I believe.) If verification succeeds, I assign . If it fails, I stop sending ECNECT0 marked packets, and I stop trying to verify any future ACK-ECN information.
NAME ping?
Sorry, long weekend in Germany. Yep this sounds right. If you only have ECT(0), you can simplify test 2 to just do what you do in 3 ('ecninc[ECNECT0] + ecncnt[ECNCE] < newack[ECNECT0]') and for test 3 you just need to check if there are any ECT(1) marking at all and fail if you ever get feedback that ECT(1) was observed.
Maybe use <= rather than < though.
NAME I'm not sure that's correct?
Oh yes, didn't see that. I also think it should be <= . NAME why do think it should be < ?
Because if the number of marked packets in the ACK is == the number of marked packets I sent, that is not a verification failure? (Or am I misunderstanding something?)
Right... I got confused that the condition is about moving into failure mode.

So usually the numbers should always be exactly equal; only if some marked packets get not ack'ed at all the received count could be larger than the number of marked sent and ack'ed packet. I guess, if you know that this is not the case you could be even stricter. Otherwise if there are no unack'ed packets and the received count would be larger, it would mean you have some remarking from Not-ECT to ECT or CE. That less a problem in terms of hiding congestion but might still indicate that something is wrong... however, given that you mark all packet in your implementation with ECT(0) that case should not happen.
And as noted the primary failure mode where increase of reference counter can be larger than newely ACKed packets is lost ACKs. Section 13.4.1 is clear on that duplicate packets shall be surpressed prior to increasing the relevant ECN counter.