Subject: PR #3547: community input needed

From: Martin Duke <[email-masked]>
Date: Thu, 2 Apr 2020 17:15:53 -0700

Hello QUIC,

We have had an endless discussion on #3547
<https://github.com/quicwg/base-drafts/pull/3547>, and Kazuho, MT, and I
have gotten to the point where everyone else is exhausted, the decision
space is opaque to newcomers, and we're making value judgments about
implementer pain. So I'd like to try to extract out the key issue and see
if we can get some broader input about these value judgment.

*Issue*: in the current spec, there is a legal attack
<https://github.com/quicwg/base-drafts/issues/3509> where the attacker
sends NEW_CONNECTION_ID at a very high rate, with each frame increasing
Retire Prior To enough to remain compliant with the victim's
active_connection_id_limit. The victim is required to send a large number
of RCIDs, some of which may be lost or not acked. There is essentially
unbounded state at the victim to keep track of this, and this state is
useless when the peer is not behaving pathologically. (There are other
variants of this, but I'll keep it simple)

We are also trying to do this without breaking changes that make the coming
draft-28 incompatible with draft-27, which is why we've ruled out a fix to
the wire image <https://github.com/quicwg/base-drafts/pull/3553> that would
comprehensively solve the problem.

We've converged on two tweaks to the spec, which I'll call "the leak" and
"the lag".

*"the lag"*: instead of telling the host to send all RCIDs right away,
suggest that it can limit the number of RCIDs it sends per RTT to a small
number that can easily fit in a packet. This comment
<https://github.com/quicwg/base-drafts/pull/3547#discussion_r401340815>has
pseudocode that implements one way to do this where the required state is
approximately constant for any number of RCIDs to retire.

*Pro*: a very subtle tweak to the spec that doesn't change anything about
the contract with the peer -- it already has to handle the possibility that
RCIDs might be delayed.
*Con*: The low-state pseudocode is a little cute and may not mesh well with
existing implementations.

*"the leak"*: the other approach is to allow hosts in this situation to
limit the number of RCIDs it has outstanding to a reasonable number and
simply *never send* some RCIDs beyond this limit.

*Pro*: This is likely to be easier to retrofit onto existing
implementations. It's also conceptually simpler to figure out how to do
this while limiting state.
*Con:* If the peer never receives some RCIDs, this will eventually starve
its ability to provide new ones and may therefore force termination of the
connection.

There is other stuff in here but this is the root of it. If there is a
strong signal from the list I think Kazuho and I will stop arguing and ship
it.

Martin Duke

---

From: Kazuho Oku <[email-masked]>
Date: Fri, 3 Apr 2020 16:13:58 +0900

FWIW, I do not think the two approaches are exclusive to each other.

I would say that "leak" is a superset of "lag."

What the "leak" approach says is that the state that an endpoint needs to
retain is more than X, where X is 2 * max_connection_id_limit.

If an endpoint can retain any number of CIDs being retired in a limited
amount of state, then that's fine. That's following the recommendation.

The risk of leaking some retirements is that an unlucky endpoint might run
out of CIDs earlier than in the ideal case. But I do not think that is an
issue.

That is because peers are allowed to limit the number of CIDs they offer,
and the therefore endpoints are expected to see peers not providing enough
CIDs (or stop providing any CIDs at all). That's a norm in QUICv1 / H3.
Clients would simply reconnect to the server using 0-RTT.

Considering that, I do not think we need to require people to implement the
"lag" logic, which expects a particular data structure in how they retain
the CIDs.

2020年4月3日(金) 9:17 Martin Duke <[email-masked]>:

> We are also trying to do this without breaking changes that make the
> coming draft-28 incompatible with draft-27, which is why we've ruled out a fix
> to the wire image <https://github.com/quicwg/base-drafts/pull/3553> that
> would comprehensively solve the problem.
>
> *Con*: The low-state pseudocode is a little cute and may not mesh well
> with existing implementations.
>

-- 
Kazuho Oku

---

From: Mike Bishop <[email-masked]>
Date: Fri, 10 Apr 2020 20:08:52 +0000

Piling on very late to this thread, as I’ve already said my piece on the issue and suggested a less-refined version of the Martins’ “lag” proposal.  I do not like the idea that an endpoint is going to drop state and never retire it, particularly since the CID issuer MAY close the connection when this happens.  (Assuming it’s an unlucky loss pattern and not a malicious peer, of course.)

This is solvable in constant space in implementations.  We don’t need to specify the mechanism that an implementation must use; we just need to note that the attack exists and that implementations should bound the state they use while continuing to send the required frames.  And indeed, #3547 says that endpoints SHOULD limit their state commitment (with a suggested lower bound for that limit), MUST NOT forget CIDs before retiring them, and MAY treat too many outstanding retirements as an error.

There’s a separate editorial question of whether we want to outline this reasonable defense, but I don’t think anyone is suggesting that we require this particular implementation.  The proposed text allows for other designs, such as terminating the connection if the state commitment exceeds your limit.

From: QUIC <[email-masked]> On Behalf Of Kazuho Oku
Sent: Friday, April 3, 2020 3:14 AM
To: Martin Duke <[email-masked]>
Cc: IETF QUIC WG <[email-masked]>
Subject: Re: PR #3547: community input needed

What the "leak" approach says is that the state that an endpoint needs to retain is more than X, where X is 2 * max_connection_id_limit.

If an endpoint can retain any number of CIDs being retired in a limited amount of state, then that's fine. That's following the recommendation.

The risk of leaking some retirements is that an unlucky endpoint might run out of CIDs earlier than in the ideal case. But I do not think that is an issue.

That is because peers are allowed to limit the number of CIDs they offer, and the therefore endpoints are expected to see peers not providing enough CIDs (or stop providing any CIDs at all). That's a norm in QUICv1 / H3. Clients would simply reconnect to the server using 0-RTT.

Considering that, I do not think we need to require people to implement the "lag" logic, which expects a particular data structure in how they retain the CIDs.

2020年4月3日(金) 9:17 Martin Duke <[email-masked]<mailto:[email-masked]>>:
Hello QUIC,

We have had an endless discussion on #3547<https://github.com/quicwg/base-drafts/pull/3547>, and Kazuho, MT, and I have gotten to the point where everyone else is exhausted, the decision space is opaque to newcomers, and we're making value judgments about implementer pain. So I'd like to try to extract out the key issue and see if we can get some broader input about these value judgment.

Issue: in the current spec, there is a legal attack<https://github.com/quicwg/base-drafts/issues/3509> where the attacker sends NEW_CONNECTION_ID at a very high rate, with each frame increasing Retire Prior To enough to remain compliant with the victim's active_connection_id_limit. The victim is required to send a large number of RCIDs, some of which may be lost or not acked. There is essentially unbounded state at the victim to keep track of this, and this state is useless when the peer is not behaving pathologically. (There are other variants of this, but I'll keep it simple)

We are also trying to do this without breaking changes that make the coming draft-28 incompatible with draft-27, which is why we've ruled out a fix to the wire image<https://github.com/quicwg/base-drafts/pull/3553> that would comprehensively solve the problem.

We've converged on two tweaks to the spec, which I'll call "the leak" and "the lag".

"the lag": instead of telling the host to send all RCIDs right away, suggest that it can limit the number of RCIDs it sends per RTT to a small number that can easily fit in a packet. This comment <https://github.com/quicwg/base-drafts/pull/3547#discussion_r401340815> has pseudocode that implements one way to do this where the required state is approximately constant for any number of RCIDs to retire.

Pro: a very subtle tweak to the spec that doesn't change anything about the contract with the peer -- it already has to handle the possibility that RCIDs might be delayed.
Con: The low-state pseudocode is a little cute and may not mesh well with existing implementations.

"the leak": the other approach is to allow hosts in this situation to limit the number of RCIDs it has outstanding to a reasonable number and simply *never send* some RCIDs beyond this limit.

Pro: This is likely to be easier to retrofit onto existing implementations. It's also conceptually simpler to figure out how to do this while limiting state.
Con: If the peer never receives some RCIDs, this will eventually starve its ability to provide new ones and may therefore force termination of the connection.

There is other stuff in here but this is the root of it. If there is a strong signal from the list I think Kazuho and I will stop arguing and ship it.

Martin Duke

--
Kazuho Oku

---

From: Mikkel Fahnøe Jørgensen <[email-masked]>
Date: Fri, 10 Apr 2020 13:35:12 -0700

Leaking CIDs rubs me the wrong way unless they can be reclaimed
automatically be seeing a more recent RCID, but that likely won’t work
since new CIDs are not received in order and thus not used in order. Having
a connection going down randomly on otherwise well behaved CID resource
management seems like a big hack.

Requiring RCID ASAP is also not good, and is the source of mentioned
attack, but could also be used to drain resources such as a batteri.

Hence I find the suggested “lag” approach natural. It fits naturally with
the concept of sending ACKs periodically, but in most cases not immediately.

If a connection terminates because RCID does not happen fast enough, either
the CID issuer is doing something wrong, or the consumer is, and then it is
fine to terminate. If both play along there shouldn’t be an issue.

As to state consumption on handling RCID, it doesn’t have to be precise, it
could just be check for outstanding CIDs whenever a connection is checked
anyway.

This leaves one question. If the “lag” is relaxed, it is still opens for an
attack if RCIDs are accidentally send too often, but I’m not too concerned
about that. Take away is don’t make strict rules, but suggest peer limits
the RCID rate when there is no other transmission activity, similar to ACKs.

Kind Regards,
Mikkel Fahnøe Jørgensen

On 10 April 2020 at 22.09.12, Mike Bishop ([email-masked]) wrote:

Piling on very late to this thread, as I’ve already said my piece on the
issue and suggested a less-refined version of the Martins’ “lag” proposal.
I do not like the idea that an endpoint is going to drop state and never
retire it, particularly since the CID issuer MAY close the connection when
this happens.  (Assuming it’s an unlucky loss pattern and not a malicious
peer, of course.)

This is solvable in constant space in implementations.  We don’t need to
specify the mechanism that an implementation must use; we just need to note
that the attack exists and that implementations should bound the state they
use while continuing to send the required frames.  And indeed, #3547 says
that endpoints SHOULD limit their state commitment (with a suggested lower
bound for that limit), MUST NOT forget CIDs before retiring them, and MAY
treat too many outstanding retirements as an error.

There’s a separate editorial question of whether we want to outline this
reasonable defense, but I don’t think anyone is suggesting that we require
this particular implementation.  The proposed text allows for other
designs, such as terminating the connection if the state commitment exceeds
your limit.

*From:* QUIC <[email-masked]> *On Behalf Of * Kazuho Oku
*Sent:* Friday, April 3, 2020 3:14 AM
*To:* Martin Duke <[email-masked]>
*Cc:* IETF QUIC WG <[email-masked]>
*Subject:* Re: PR #3547: community input needed

Hello QUIC,

Martin Duke

-- 

Kazuho Oku

---

From: Martin Duke <[email-masked]>
Date: Fri, 10 Apr 2020 14:34:22 -0700

Thanks everyone, I believe we have converged on a PR in #3547 that is
consistent with the input in this thread. Feel free to give at a positive
review (or not) at github.

On Fri, Apr 10, 2020 at 1:35 PM Mikkel Fahnøe Jørgensen <[email-masked]>
wrote:

> If a connection terminates because RCID does not happen fast enough,
> either the CID issuer is doing something wrong, or the consumer is, and
> then it is fine to terminate. If both play along there shouldn’t be an
> issue.
>
> As to state consumption on handling RCID, it doesn’t have to be precise,
> it could just be check for outstanding CIDs whenever a connection is
> checked anyway.
>
> This leaves one question. If the “lag” is relaxed, it is still opens for
> an attack if RCIDs are accidentally send too often, but I’m not too
> concerned about that. Take away is don’t make strict rules, but suggest
> peer limits the RCID rate when there is no other transmission activity,
> similar to ACKs.
>
> Kind Regards,
> Mikkel Fahnøe Jørgensen
>
>
> Considering that, I do not think we need to require people to implement
> the "lag" logic, which expects a particular data structure in how they
> retain the CIDs.
>
>
>
>
>