Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling packets with both new Destination Connection ID and 4-tuple #188

Closed
qdeconinck opened this issue Mar 6, 2023 · 11 comments
Closed

Comments

@qdeconinck
Copy link
Contributor

Taking side-discussion of #169 in its own issue.

I think we can use path ID mechanism of the current draft with the addition of the following rules to address the issues discussed in this thread:

(1) Addressing the corner case when new CID and NAT rebinding happen at the same time. When an endpoint receives a packet that has (new CID, new tuple), check if the packet is trying to initialize a new path (i.e., whether it contains a PATH_CHALLENGE). If it has PATH_CHALLENGE, try creating a new path. If it has no PATH_CHALLENGE and we don't know which path the packet is associated with (num. of paths>=2), discard the packet. If there is only one path, we perform path validation as single path QUIC does.

@huitema
Copy link
Contributor

huitema commented Mar 6, 2023

I think a new four tuple is kind of by definition a new path. I don't like trying to reason whether packets that do or don't carry a path challenge. The receiver of this packet should definitely send challenges, validate the path, etc.

Beyond that, the concern is performance, and heuristics such as "this new path looks a lot like a previous path that was seen before, with a different CID and almost the same 4-tuple (e.g., only port number changed), so maybe it could be initiated with the RTT and congestion control data of this previous path." I think the receiver of the path-creating packet MAY implement a logic like that, but it is not mandatory.

Consider that the main usage is a not that was silent for a long time, and resumes transmission with anew CID for privacy reasons. Being silent for a long time implies that the RTT and congestion conditions may very well have changed...

@yfmascgy
Copy link
Contributor

yfmascgy commented Mar 8, 2023

I think we should require that a PATH_CHALLENGE to be included when creating a new path. Otherwise when a sender regularly rotates CID, an attacker can know this and tweak the 4-tuple each time. It might create a lot of path states as @qdeconinck mentioned in the #169 .

@huitema
Copy link
Contributor

huitema commented Mar 8, 2023

I don't think we should add extra frames when rotating CID, because:

  1. Attackers can already change 4-tuple even if the CID does not rotate. This is actually a powerful attack against QUIC. If executed well, it allows the on path attacker to redirect traffic through a path of its choosing. The root cause is that encrypting the UDP content does not protect the IP header. We would need to add the 4 tuple to the packet authentication, and doing that while also supporting NAT rebinding is really hard.

  2. This would change the semantics of the PATH CHALLENGE frame from "verifying continuity" to "verifying continuity and asserting a new path". Yet there are many legitimate usage of PATH CHALLENGE without any path change,

  3. In the CID rotation scenario, the sending node is not changing its congestion window, and will send a full CWIN of packets before getting any acknowledgement on the new path. Even if the first packet carried a PATH CHALLENGE, that packet could be lost, and the receiver will have to process the second packet, which will typically only carry stream frames.

@mirjak
Copy link
Collaborator

mirjak commented Mar 8, 2023

So the problem we initially discussed was that the two peers could end up with a different view on how many path we have. However, with the "loose" model we have right now, I don't think that's actually a problem.

@yfmascgy
Copy link
Contributor

yfmascgy commented Mar 9, 2023

@huitema Maybe the earlier question was not clear. My point is that, yes we do not include additional frames when rotating CID on path, which basically aligns with what you said. However, I think when a peer wants to create a new path(with a new 4-tuple and a new CID), it needs to include a PATH_CHALLENGE in the first packet and this is what we do in the "Example of new path establishment". And if the receiver does not see a PATH_CHALLENGE from a packet with a completely new CID and a completely new 4-tuple, it should simply discard this packet and not enter the path creation flow.

@huitema
Copy link
Contributor

huitema commented Mar 9, 2023

@yfmascgy if you "do not include additional frames when rotating CID on path" and the NAT rebinding happens just at this time, then "receiver does not see a PATH_CHALLENGE from a packet with a completely new CID and a completely new 4-tuple" and will discard it. That does not seem right, unless we are a lot more specific about what we consider "completely new" -- if it is only a port change, is that completely new? If both port and address change but the address is in the same /24, is that completely new?

We have a parallel work on clarifying the migration issues with PR #172, which rightly states that the main issue is what happens with the old path. If the CID renewal occurs without rebinding, everything works as expected. But if the 4-tuple changes and the server does not recognize this as a renewal, the server will keep sending some packets to the old path. As @mirjak says, the problem will fix itself when that old path is somehow closed -- but it would be better if the server abandoned the old path sooner.

@qdeconinck
Copy link
Contributor Author

I think the only (performance) issue we have now with such event is the one where the server may still use the "old" path while the "new" one is actually the rebinding of the "old". We could address this by asking the client to be explicit by sending a PATH_ABANDON frame with a specific error code, but the draft would then need to define such a code.

@huitema
Copy link
Contributor

huitema commented Mar 9, 2023

Here is what I understand so far. There are four options, all with some drawbacks:

  • server notices that there is no "PATH_CHALLENGE" in the packet, which means the client probably did not intend to create a new path. That may be a fine heuristic, but it does not tell the server which path was the old path. The server will have to do some guesswork, e.g., look for three-tuple matching (destination-IP, destination-port, source-IP), or for two-and-a-half-tuple matching (destination-IP, destination-port, prefix of source-IP). It would then do as suggested in "NAT Rebinding", i.e., challenge both paths and pick the one that responds.

  • client bundles a RETIRE_CONNECTION_ID(old CID) with the first packet after the migration. This has a major drawback: it prevents the server from sending new acknowledgements for packets received on the old path.

  • client bundles a PATH_ABANDON(old CID, reason = CID RETIRED) with the first packet after the migration. This creates a possible confusion, because PATH_ABANDON normally means abandon the path, not abandon the CID. We could see implementation errors.

  • client bundles a newly defined ABANDON_CID(old CID) frame with the first packet after the migration. The server realize that the client was merely trying to rotate the CID but that a path rebinding happened, and avoids sending new packets on the old path.

The last option is the best: indicate clearly the client intent, avoid extra traffic, avoid potential side effects. But all bundling solution have the issue that this "first packet" may be lost, and that the net packet will be received without the bundled data. This is probably OK since we are trying to solve a performance issue: the old path will be abandoned eventually, after idle timeout or after receiving a RETIRE_CONNECTION_ID frame. Or, if the first packet with the new CID does not include the ABANDON_CID frame, the server may fall back to the first option: notice that there is no path challenge, etc.

@kazuho
Copy link
Member

kazuho commented Jul 25, 2023

+1 to what @huitema said in his latest comment.

IMO, regardless of changing the path or not, endpoints SHOULD send path statuses when it starts using a new Connection ID.

If we haven't already said that in the draft, then we should.

Then, it does not matter if NAT rebinding happens at the same time.

@gloinul
Copy link

gloinul commented Jul 25, 2023

So I think one issue that is not clear enough in the document in relation to this is how to peer A that transmit packets that goes through a network and arrives a B with a different 5-tuple for this CID than previous. Based on RFC 9000 connection migration this will cause a path validation being sent by B to A. However, I think the multipath document needs to be clearer on how this is dealt with in A as it is not a connection migration like it would be in single path QUIC. So there is a clear difference here.

Also maybe I haven't read the text back and forth, but it is not clear to me if the above scenario A will change its CID, or if will continue to use the CID it was using? This appears relevant for the reason in that A can't determine if this path change only results was the result of a basic NAT binding with a source port change from B's perspective. But, I think there are a couple of corner cases, like two layer NATs with some failover where the packet could actually exit through a different NAT closest to B than previous and more substantially be a different route. So there might be some question here if one should ensure a CID change and discard any path congestion state.

@mirjak
Copy link
Collaborator

mirjak commented May 14, 2024

Closing this issue as addressed by PR #292

@mirjak mirjak closed this as completed May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants