Draft: draft-ietf-quic-datagram

Commentary: Clarify meaning of 0 in the datagram transport parameter , based on the discussion at IETF 110 . This follows one of the suggestions to have 0 mean no datagrams , and 0 mean support for datagrams . This should be a non - breaking change with existing implementations . 
 This change is very surprising . Why not take the approach that was discussed in the issue , wherein the limit describes the payload size , and presence of the transport parameter indicates extension support ? It 's not useful to have a bunch of ill - formed possibilities where the limit describes the frame size but is smaller than needed to encode the frame . See also 
 this is based on the WG discussion , minuted here : 
 So it comes down to preserving the weird semantics nobody wants because renumbering the TP is aesthetically unappealing ? That seems like a shame . Unless saying " I support it , but you ca n't send it " is actually useful somehow ? 
 Right now , the TP specifies a maximum frame size , including frame type , length and payload . This makes certain values invalid ( 0 , 1 ? ) . Also , since this values practically is a kind of flow control , indicating how much data I 'm willing to receive at a time , it 's the payload length that 's important here , not the framing . For these reasons , I 'm arguing to change this to specifying a maximum payload length . Then , the question of what a value of zero means . Should a value of 0 be the same thing as not present or should it mean that only 0 length datagrams are allowed ? I think it is simpler to say that a value of zero is the same as not present ( i.e. disabled ) . ( Issue copied from , by on 2019 - 11 - 18 ) 
 from on 2019 - 11 - 18 : Was n't the legality of zero - length frames agreed upon in I did n't even realize that the parameter was n't payload size . Strongly agree that it should be . 
 from on 2019 - 11 - 19 : I agree that is simpler to define zero to mean not allowed , but it is highly confusing to be forced to require a length of one if you specifically only use 0 length datagrams for heatbeats or similar . It would be better to a have separate indicator to reject datagrams altogether . 
 Is n't this indicated by omitting the transport parameter entirely ? 
 If omitting the parameter indicates that data frames are not permitted , then this achieves the purpose , yes . So there is no need to have 0 means disabling . Hence it is better to have 0 mean 0 length , but still valid . This is the most natural representation , and there is a an actual meaning ful use of these , namely heartbeats . Further , by making it explicit that length 0 is the only valid , you can have a fast or lightweight implementation . 
 I wonder if we need this limit at all . It introduces implementation complexity and it 's unclear why any implementation would like to limit the size of DATAGRAM frames ( or payloads ) they can receive . We already have for implementations that have a limit on their stack size . Does anyone have a use - case where they 'd like to limit the size of DATAGRAM frames ( or payloads ) specifically ? 
 I 'm all for removing the limit . As I have it coded up in  , I always send a  value . It 's not even configurable by the app . I do n't think " I have a buffering limit " a good reason to have a limit . It 's limited by a single packet already . That should be enough IMO . 
 I do n't think it 's particularly useful to set this smaller than a packet , given that a reasonable buffer is bigger than that , and I 'm not sure what a peer could usefully do with the information that the limit is larger than a packet . : +1 : 
 The more I think about it , limiting the size of the QUIC DATAGRAMS ( frames or payloads ) is weird . In part because there is no wiresignal to describe a  number of DATAGRAMS per packet , and we seem to have discounted the need for an internal API to configure how frames are composed in a packet . I do n't hear anyone with use cases ; removing the limit removes a whole load of edge cases and/or implementation defined behaviour . I think the simplification would be a win . 
 One reason you might want to limit Datagrams is if you have a slottet buffering system for datagrams , for example 1 K , or 256 bytes per datagram , or a paged system of multiple datagrams . In this case you can not always fit a whole package into the buffering . For streams you have an entirely different mechanism , and other frames are generally not large . 
 that 's an interesting thought - is this something you 're planning on implementing and have a use for , or is it more of a thought experiment ? 
 Unfortunately I 've had to push back on my QUIC dev , but I did work with an emulation of netmap , on top of select / poll , that uses slots to receive packets from the OS without context switching . Sometimes a packet needs to span multiple slots , but often it just drops into a 2 K slot or so . I could imagine many use cases where datagrams are redistributed to other threads or processes in a similar manner without having an explicit use case . I believe there are now several kernel interfaces that uses a similar approach . 
 As far as I know , those kernel interfaces all use slots that are larger than the interface MTU , so I do n't think they 'd require reducing the payload size . Am I missing something ? 
 Well , I can only speak for netmap , but it works by having the user specify one large memory block divided into a number of equal sized slots of a user specified size . Each slot has a control record with some flags . A record can indicate that the slot is partial and reference the next slot in the packet . So you can can choose a slot of 64 K and waste a lot of memory , or choose a slot of 512 bytes and frequently have to deal with fragmenting . Or chose 2 K for a use case where you know that you will not have larger payload and can afford the space overhead . EDIT : to clarify , it is the network driver that chooses to link data and update the control record when receiving , and the user process when writing . I 'm not sure exactly how the other kernel interfaces behave but from memory it is rather much the same . For QUIC datagrams it is a bit different in that you likely want to avoid fragmentation at all cost due to the complexity and the non - stream nature of the data . If you could afford fragmentation it would make less sense to have a limit on the size . 
 I support switching to TP specifying  payload size . 
 IIRC , the original reason this limit was introduced is to allow QUIC proxies to communicate the  datagram size it gets from the MTU between itself and the backend . 
 In my experience , a MASQUE proxy does n't know that information at the time it opens a listening QUIC connection . , and there is nothing to say that all backends would share the same value . 
 This is not for MASQUE , but for a direct QUIC - to - QUIC proxy . 
 OK , do you expect the  DATAGRAM frame size a backend expects to receive is smaller than the QUIC packet size it is willing to receive ? 
 If we do have a  frame / perhaps size still defined , perhaps it makes sense to let this be updated by a new frame ( MAXDATAGRAMSIZE ) . 
 A MAXDATAGRAMSIZE frame would be tricky because of reordering . In QUIC , all the MAX_FOOBAR frames can only increase limits , but here I suspect for this to be useful we 'd want to be able to lower the limit which greatly increases complexity . 
 I expect this to be fairly uncommon , but whenever this does happen , it would make the entire connection unreliable for datagrams , meaning the applications would have to do their own MTU discovery on top of the one QUIC already does . 
 The default ought to be " whatever fits in the PMTU " . 
 Yes , you have to do PMTUD . ( What is pernicious here is that if you get too good at that , the value that you resolve to might not be good forever because your varints will use more bytes over time and so reduce available space . ) 
 which varints will use more bytes here ? 
 I was thinking about flow IDs . If you are not reusing flow IDs , there is a good chance that those will get bigger over time . But I guess that assumes use of flow IDs . Mostly , I guess that DATAGRAM can be each sent in their own packet / datagram , so the size of other frames wo n't affect the space that is available . 
 Yeah the flow ID is part of the payload as far as this document is concerned . Our implementation will try to coalesce DATAGRAM frames with other frames , but that does n't impact the  possible payload size because if the DATAGRAM frame does n't fit with other frames we just send it in its own packet .
Support for receiving the DATAGRAM frame types is advertised by means
of a QUIC Transport Parameter (name=max_datagram_frame_size,
value=0x0020).  The max_datagram_frame_size transport parameter is an
integer value (represented as a variable-length integer) that
represents the maximum size of a DATAGRAM frame (including the frame
type, length, and payload) the endpoint is willing to receive, in
bytes.  An endpoint that includes this parameter supports the
DATAGRAM frame types and is willing to receive such frames on this
connection.  Endpoints MUST NOT send DATAGRAM frames until they have
received the max_datagram_frame_size transport parameter.  Endpoints
MUST NOT send DATAGRAM frames of size strictly larger than the value
of max_datagram_frame_size the endpoint has received from its peer.
An endpoint that receives a DATAGRAM frame when it has not sent the
max_datagram_frame_size transport parameter MUST terminate the
connection with error PROTOCOL_VIOLATION.  An endpoint that receives
a DATAGRAM frame that is strictly larger than the value it sent in
its max_datagram_frame_size transport parameter MUST terminate the
connection with error PROTOCOL_VIOLATION.  Endpoints that wish to use
DATAGRAM frames need to ensure they send a max_datagram_frame_size
value sufficient to allow their peer to use them.  It is RECOMMENDED
to send the value 65535 in the max_datagram_frame_size transport
parameter as that indicates to the peer that this endpoint will
accept any DATAGRAM frame that fits inside a QUIC packet.
Support for receiving the DATAGRAM frame types is advertised by means
of a QUIC Transport Parameter (name=max_datagram_frame_size,
value=0x0020).  The max_datagram_frame_size transport parameter is an
integer value (represented as a variable-length integer) that
represents the maximum size of a DATAGRAM frame (including the frame
type, length, and payload) the endpoint is willing to receive, in
bytes.
The default for this parameter is 0, which indicates that the
endpoint does not support DATAGRAM frames.  A value greater than 0
indicates that the endpoint supports the DATAGRAM frame types and is
willing to receive such frames on this connection.
An endpoint MUST NOT send DATAGRAM frames until it has received the
max_datagram_frame_size transport parameter with a non-zero value.
An endpoint MUST NOT send DATAGRAM frames that are larger than the
max_datagram_frame_size value it has received from its peer.  An
endpoint that receives a DATAGRAM frame when it has not indicated
support via the transport parameter MUST terminate the connection
with an error of type PROTOCOL_VIOLATION.  Similarly, an endpoint
that receives a DATAGRAM frame that is larger than the value it sent
in its max_datagram_frame_size transport parameter MUST terminate the
connection with an error of type PROTOCOL_VIOLATION.
For most uses of DATAGRAM frames, it is RECOMMENDED to send a value
of 65535 in the max_datagram_frame_size transport parameter to
indicate that this endpoint will accept any DATAGRAM frame that fits
inside a QUIC packet.
Indicates that the connection should enable support for unreliable
DATAGRAM frames.  An endpoint that advertises this transport
parameter can receive datagrams frames from the other endpoint, up
to and including the length in bytes provided in the transport
parameter.
A non-zero value indicates that the endpoint supports receiving
unreliable DATAGRAM frames.  An endpoint that advertises this
transport parameter can receive DATAGRAM frames from the other
endpoint, up to and including the length in bytes provided in the
transport parameter.  The default value is 0.