Subject: [quicwg/base-drafts] Small packets don't trigger a stateless reset (#1681)

From: Christian Huitema <[email-masked]>
Date: Thu, 16 Aug 2018 21:24:09 -0700

Suppose that a server uses 4-bytes connection ID. The client wants to test whether the server is still present, and sends a "PING". The packet will include:
* First octet: 1 byte
* Dest CNX-ID: 4 bytes
* PN: 1 byte (PNE encrypted)
* Ping frame: 1 byte (encrypted)
* AEAD checksum: 16 bytes.
* Total: 23 bytes.

The server does not recognize the connection ID -- memory of that connection is lost. It will compose a stateless reset packet that include, per draft 14:
* First octet: 1 byte
* At least 20 bytes with randomized or unpredictable value
* 16 bytes Reset Secret.
* Total: at least 37 bytes.

But then the stateless reset becomes larger than the incoming packet, and draft-14 says that:  _An endpoint SHOULD NOT send a stateless reset that is significantly larger than the packet it receives.  Endpoints MUST discard packets that are too small to be valid QUIC packets.  With the set of AEAD functions defined in [QUIC-TLS], packets less than 19 octets long are never valid._

I understand that "significantly larger" is a weasel word that can give some leeway. But then, too much leeway will allow attackers to seed a game of "reset ping pong" between two servers. On the other hand, the "ping" example above is exactly what we want to address with stateless reset. So what gives? 

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681

---

From: Martin Thomson <[email-masked]>
Date: Thu, 16 Aug 2018 21:30:36 -0700

Pad your pings.  That's the best that I can recommend.  The gap between the smallest valid packet and the smallest packet that will reliably generate a stateless reset is 18 octets (the maximum size connection ID length).  Adding at least that much padding is probably a good idea if you want a timely response.

Stateless reset is an optimization, we agreed that when presented with the choice between the optimization and the risk of playing an eternal game of ping pong, the optimization loses.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413754443

---

From: Christian Huitema <[email-masked]>
Date: Thu, 16 Aug 2018 21:38:58 -0700

So basically you are saying that packets that are at least 38 bytes should not elicit a stateless reset, because the minimal valid reset size is 37 bytes. And that senders who want to test the continued presence of a peer should ensure that their packets are at least 38 bytes long, using padding if necessary.

But that's not what the draft says. It says _"packets less than 19 octets long are never valid"_ .Which can be interpreted as _"packets at least 19 octets long may elicit a stateless reset, and yes 37 bytes is larger than 19, but not significantly larger"_.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413755378

---

From: Martin Thomson <[email-masked]>
Date: Thu, 16 Aug 2018 21:43:35 -0700

The draft allows for an endpoint to be stateful and produce a stateless reset for a smaller incoming packet.  If an endpoint wants to remember how many stateless resets it has sent to peers, then it can avoid the ping pong match because it can cut it off.  (Note: stateful from the event onwards, not implying that it needs to carry state across the event.)

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413755932

---

From: Christian Huitema <[email-masked]>
Date: Thu, 16 Aug 2018 22:56:37 -0700

I ended up adding code to ensure that all packets are at least 38 bytes long -- the padding option that you suggested. It is not ideal, but I can rationalize it. For example, it prevents packet size analysis of small packets. It might make sense to describe this padding option somewhere, maybe in section 6.13.4.3, Looping.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413765506

---

From: mirjak <[email-masked]>
Date: Fri, 17 Aug 2018 03:37:09 -0700

Actually I don't really think that this is a problem for ping. You send a ping when you don't have any other data to send and want to test if the other end is still alive. Not getting anything back if the other end does not have state anymore is exactly what you want...

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413826597

---

From: Marten Seemann <[email-masked]>
Date: Fri, 17 Aug 2018 03:48:06 -0700

You'd want to know that as soon as possible, not after TLPing and RTOing the ping and then running into a timeout.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413828792

---

From: MikkelFJ <[email-masked]>
Date: Fri, 17 Aug 2018 03:53:35 -0700

This is why I have argued for a statistical reset instead of a purely size based solution. If you are below a certain size, you only send a reset with less than 50% probabiity or so. This is much simpler than padding etc. and does not prevent fast reset above the size limit.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413829892

---

From: Christian Huitema <[email-masked]>
Date: Fri, 17 Aug 2018 10:49:27 -0700

@mikkelfj  Statistical reset is generally fine, but it will only solve the "ping" scenario half the time. The other half, there will be a timeout. So it certainly makes sense to at least "pad the ping".

Also, writing tests for statistical behavior is rather painful. Deterministic behavior is much easier to test and get right.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413940784

---

From: MikkelFJ <[email-masked]>
Date: Fri, 17 Aug 2018 11:13:57 -0700

If you de facto demand that packets are larger than the smallest reset, you might as well flag the reset packet as being a reset in the first place, at least for small packets.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413947479

---

From: Christian Huitema <[email-masked]>
Date: Fri, 17 Aug 2018 13:06:09 -0700

@mikkelfj Good point. That's an inherent draw back of the length based solution. Now we just need a solution that's not length based, so it cannot be spied over, and also prevents infinite ping pong.

One way would be some kind of rate control. "Don't send a reset to destination X if a previous reset was sent recently". But this kind of things have their own issues. In the "server reboot" scenario, the restarting server may expect to receive a bunch of packets from old connections.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413974795

---

From: MikkelFJ <[email-masked]>
Date: Fri, 17 Aug 2018 13:11:43 -0700

But realistically, I think the statistical concern is overrated. If you need to reset due to small packets like PING, it is likely because a long running low volume connection is doing keep-alive, or because a fast runnig low volume connection is running like player positio in a game.

In the first case it does not matter that disconnect takes a long time, just that it eventually happens. In the second case, the packet rate is high and statistics will catch up very soon.

For other cases, it is likely that packet content is larger, such as a HTTP request.

Even if there are other cases where a reset is needed faster, it is probably not that big of a problem, and if it is, padding can be used to override statistics.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413975971

---

From: MikkelFJ <[email-masked]>
Date: Fri, 17 Aug 2018 13:21:04 -0700

Not related to this issue, I am also concerned about the sending a slightly smaller packet when possible. I'd like to see some numbers or a simultation because I think this can be a signfiicant amplification attack. This could be mitigated by reducing the packet size by a factor 2 at least 50% of the cases. If the packet size is just reduced by a single byte, the amplification could be a factor 1000.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413978074

---

From: Christian Huitema <[email-masked]>
Date: Fri, 17 Aug 2018 13:26:45 -0700

I understand that statistical solution can work in practice. Not so good with sparse traffic, but well enough with long running connections that send a bunch of packets. But statistical solutions are a pain to test. Your tests end up like "try N times and verify that it works" -- but then still fail once every 2**N trial. 

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#issuecomment-413979329

---

From: Martin Thomson <[email-masked]>
Date: Tue, 21 Aug 2018 20:26:18 -0700

Closed #1681 via #1682.

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/quicwg/base-drafts/issues/1681#event-1801671841