Change:
and
Thanks Christian, Your comments ended up as an editorial rewrite of the section, with port verification added.
The diagrams indicate they are in the range 0..160. This is incorrect.
SS Tokens can have this, because they could be NEW_TOKEN tokens. But the shared state format is limited to 64.
Fixed by
A few more issues with Retry tokens: For Retry, we're supposed to check the client port. Because this isn't true for NEW_TOKEN, we probably can't put it in the shared-state pseudoheader. Relatedly, we currently just say that the shared-state Retry Service has to be behind any NAT. This isn't enough. For non-shared-state, we're probably fine. For a Retry, the NAT will keep the 4-tuple binding so that on either side there is something to validate. For shared-state: if the service is in front of the NAT, the server can't validate the address. if the service is behind the NAT, it'll "work" but to add any value at all there really has to be a port in there somewhere. We can probably assume that the alternate path that creates the need for shared state won't cause a service-generated token to suddenly appear on the unprotected path.
Fixed by
We should tighten up the rules for Resumption Token processing by the Retry Service. When active, it should reject the packet, but it should send Retry. I believe we can distinguish resumption from Retry because the CID length fields are zero; as any Retry token must have a ODCIDL of at least 8, this would appear to be robust. On a related note, the requirement on servers to encode a way to distinguish the two token types is silly, because of this propery.
Fixed by
Currently the Initial Packet number MAY be encoded in the Retry Token. We must either: Include language that a server MUST NOT reject a token because this information is not present; OR Just make it part of the format. (IMO this would be bad, because then the Retry service would have to decrypt the packet number)
Most changes appear editorial, except for the addition of the port number field. If you add that port, you need to also specify how it should be processed!

Discussion:
ODCIL: The length of the original destination connection ID from the triggering Initial packet.  This is in cleartext to be readable for <del> the server, but authenticated later in the token. </del> <ins> the server, but authenticated later in the token.  The Retry Service SHOULD reject any token in which the value is less than 8. </ins> RSCIL: The retry source connection ID length.

The token protection uses an 128 bit representation of the source IP address from the triggering Initial packet.  The client IP address is <del> 16 octets.  If an IPv4 address, the last 12 octets are zeroes.  If there is a Network Address Translator (NAT) in the server </del> <ins> 16 octets.  If an IPv4 address, the last 12 octets are zeroes. If there is a Network Address Translator (NAT) in the server </ins> infrastructure that changes the client IP, the Retry Service MUST either be positioned behind the NAT, or the NAT must have the token <del> key to rewrite the Retry token accordingly. </del> <ins> key to rewrite the Retry token accordingly.  Note also that a host that obtains a token through a NAT and then attempts to connect over a path that does not have an identically configured NAT will fail address validation. </ins> The 96 bit unique token number is set to a random value using a cryptography- grade random number generator.

RSCIL: The retry source connection ID length.  Tokens in NEW_TOKEN frames MUST set this field to zero. <ins> Port: The Source Port of the UDP datagram that triggered the Retry packet.  This field MUST be present if and only if the ODCIL is greater than zero.  This field is therefore always absent in tokens in NEW_TOKEN frames. </ins> Original Destination Connection ID: The server or Retry Service copies this from the field in the client Initial packet.

POSIX time (see Sec.  4.16 of TIME_T). Opaque Data: The server may use this field to encode additional <del> information, such as congestion window, RTT, or MTU.  Opaque data MAY also allow servers to distinguish between retry tokens (which trigger use of certain transport parameters) and NEW_TOKEN frame tokens. </del> <ins> information, such as congestion window, RTT, or MTU.  The Retry Service MUST have zero-length opaque data. </ins> Some implementations of QUIC encode in the token the Initial Packet Number used by the client, in order to verify that the client sends the retried Initial with a PN larger that the triggering Initial. Such implementations will encode the Initial Packet Number as part of <del> the opaque data. </del> <ins> the opaque data.  As tokens may be generated by the Service, servers MUST NOT reject tokens because they lack opaque data and therefore the packet number. </ins> 6.3.1.

the AEAD checksum will only match if the client IP Address also matches.) <ins> If the token checksum passes, and the ODCIL and RSCIL fields are both zero, then this is an expired NEW_TOKEN token generated by the server.  An active Retry Service MUST drop the packet and issue a Retry packet with the appropriate token. </ins> The service SHOULD drop packets if the Timestamp of the retry token points to a past date; however, in order to allow for clock skew, it SHOULD NOT drop packets if the Timestamp merely points to a a few

When issuing Retry or NEW_TOKEN tokens, the server MUST include the client IP address in the authenticated data as specified in token- <del> protection-with-aead.  It MUST include a means of distinguishing service-generated Retry tokens, server-generated Retry tokens (if different), and NEW_TOKEN tokens. </del> <ins> protection-with-aead.  The ODCIL and RSCIL fields are zero for NEW_TOKEN tokens, making them easily distinguishable from Retry tokens. </ins> The server MUST validate all tokens that arrive in Initial packets, as they may have bypassed the Retry service.