martinduke / draft-duke-quic-load-balancers

An internet draft to standardize the way that QUIC servers and load balancers can support routable, unlinkable connection IDs
Other
2 stars 4 forks source link

Stateless Load Balancing of New Connections #43

Open nibanks opened 5 years ago

nibanks commented 5 years ago

I've been looking at load balancing more lately and specifically trying to figure out an algorithm to use to statelessly load balance new connections that doesn't expose any type of attack surface.

The closest thing I've come up with is something like this:

int serverId;
if (packet.IsInitial()) {
    serverId = hash(key, packet.destCid)
} else {
    serverId = packet.destCid.ExtractServerId()
}

ExtractServerId will rely on whatever encoding scheme was chosen and get the server ID from that.

My problem with the above pseudocode is post initial Initial packets. The first Initial packet will use the client chosen CID, but after that it uses the server CID. That change in CID will break the above logic. Because of this, I've actually been considering opening a transport issue recommending all Initial packets use the client CID.

Is there another way to achieve this goal? And how much of this kind of stuff should be included in the QUIC-LB spec?

P.S. How would unknown (experimental?) version numbers factor into the above pseudocode? Just go to the else?

martinduke commented 5 years ago

I would suggest the following:

serverId = packet.destCid.ExtractServerId()
if (serverId is not in pool) || (cid is too short to extract) {
    if (pkt.isInitial() || pkt.is0RTT()) {
          serverId = hash(key, packet.destCid)
    } else {
          drop
    }

This design also cleanly handles Initial DCIDs that came from RETRY.

I agree it would be nice to explain what LBs should do about this. Section 3 is not very good: ... The load balancer SHOULD route Initial and 0-RTT packets from the client using an alternate algorithm. Note that the SCID in these packets may not be long enough to represent all the routing bits. This algorithm SHOULD generate consistent results for Initial and 0RTT packets that arrive with the same source and destination connection ID. The load balancer algorithms below apply to all incoming Handshake and 1-RTT packets. ...

nibanks commented 5 years ago

Actually, after discussing with the WG, I'd prefer:

int serverId;
if (packet.IsLongHeader()) {
    serverId = hash(key, packet.tuple + packet.srcCid)
} else {
    serverId = packet.destCid.ExtractServerId()
}
igorlord commented 5 years ago

@nibanks You mean the following, right? if (packet.IsLongHeader()) {

nibanks commented 5 years ago

Yes. You are correct. Very important distinction! Thanks for catching that. I will update that comment.

igorlord commented 5 years ago

You may want to be more tricky than serverId = hash(key, packet.tuple + packet.srcCid). I am not sure what you mean by packet.tuple, but even if it is a 4-tuple, an attacker sitting on an IPv6 address foo::/96 can generate MANY srcCids that hash to the same host.

Maybe you want something like serverId = hash(key, hash(key, packet.tuple) + packet.srcCid) or, if your key can be arbitrary, serverId = hash(key+packet.tuple, packet.srcCid)