@@ -0,0 +1,558 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/proto_memory.h>
+
+#include "socket.h"
+
+/* ACK Frame {
+ * Type (i) = 0x02..0x03,
+ * Largest Acknowledged (i),
+ * ACK Delay (i),
+ * ACK Range Count (i),
+ * First ACK Range (i),
+ * ACK Range (..) ...,
+ * [ECN Counts (..)],
+ * }
+ */
+
+static struct quic_frame *quic_frame_ack_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_ping_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_padding_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_new_token_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+/* STREAM Frame {
+ * Type (i) = 0x08..0x0f,
+ * Stream ID (i),
+ * [Offset (i)],
+ * [Length (i)],
+ * Stream Data (..),
+ * }
+ */
+
+static struct quic_frame *quic_frame_stream_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_handshake_done_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_crypto_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_retire_conn_id_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_new_conn_id_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_path_response_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_path_challenge_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_reset_stream_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_stop_sending_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_max_data_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_max_stream_data_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_max_streams_uni_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_max_streams_bidi_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_connection_close_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_data_blocked_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_stream_data_blocked_create(struct sock *sk,
+ void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_streams_blocked_uni_create(struct sock *sk,
+ void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_streams_blocked_bidi_create(struct sock *sk,
+ void *data, u8 type)
+{
+ return NULL;
+}
+
+static int quic_frame_crypto_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_stream_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_ack_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_new_conn_id_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_retire_conn_id_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_new_token_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_handshake_done_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_padding_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_ping_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_path_challenge_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_reset_stream_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_stop_sending_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_max_data_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_max_stream_data_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_max_streams_uni_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_max_streams_bidi_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_connection_close_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_data_blocked_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_stream_data_blocked_process(struct sock *sk, struct quic_frame *frame,
+ u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_streams_blocked_uni_process(struct sock *sk, struct quic_frame *frame,
+ u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_streams_blocked_bidi_process(struct sock *sk, struct quic_frame *frame,
+ u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_path_response_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct quic_frame *quic_frame_invalid_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static struct quic_frame *quic_frame_datagram_create(struct sock *sk, void *data, u8 type)
+{
+ return NULL;
+}
+
+static int quic_frame_invalid_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_frame_datagram_process(struct sock *sk, struct quic_frame *frame, u8 type)
+{
+ return -EOPNOTSUPP;
+}
+
+static void quic_frame_padding_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_ping_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_ack_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_reset_stream_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_stop_sending_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_crypto_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_new_token_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_stream_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_max_data_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_max_stream_data_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_max_streams_bidi_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_max_streams_uni_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_data_blocked_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_stream_data_blocked_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_streams_blocked_bidi_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_streams_blocked_uni_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_new_conn_id_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_retire_conn_id_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_path_challenge_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_path_response_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_connection_close_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_handshake_done_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_invalid_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+static void quic_frame_datagram_ack(struct sock *sk, struct quic_frame *frame)
+{
+}
+
+#define quic_frame_create_and_process_and_ack(type, eliciting) \
+ { \
+ .frame_create = quic_frame_##type##_create, \
+ .frame_process = quic_frame_##type##_process, \
+ .frame_ack = quic_frame_##type##_ack, \
+ .ack_eliciting = eliciting \
+ }
+
+static struct quic_frame_ops quic_frame_ops[QUIC_FRAME_MAX + 1] = {
+ quic_frame_create_and_process_and_ack(padding, 0), /* 0x00 */
+ quic_frame_create_and_process_and_ack(ping, 1),
+ quic_frame_create_and_process_and_ack(ack, 0),
+ quic_frame_create_and_process_and_ack(ack, 0), /* ack_ecn */
+ quic_frame_create_and_process_and_ack(reset_stream, 1),
+ quic_frame_create_and_process_and_ack(stop_sending, 1),
+ quic_frame_create_and_process_and_ack(crypto, 1),
+ quic_frame_create_and_process_and_ack(new_token, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(stream, 1),
+ quic_frame_create_and_process_and_ack(max_data, 1), /* 0x10 */
+ quic_frame_create_and_process_and_ack(max_stream_data, 1),
+ quic_frame_create_and_process_and_ack(max_streams_bidi, 1),
+ quic_frame_create_and_process_and_ack(max_streams_uni, 1),
+ quic_frame_create_and_process_and_ack(data_blocked, 1),
+ quic_frame_create_and_process_and_ack(stream_data_blocked, 1),
+ quic_frame_create_and_process_and_ack(streams_blocked_bidi, 1),
+ quic_frame_create_and_process_and_ack(streams_blocked_uni, 1),
+ quic_frame_create_and_process_and_ack(new_conn_id, 1),
+ quic_frame_create_and_process_and_ack(retire_conn_id, 1),
+ quic_frame_create_and_process_and_ack(path_challenge, 0),
+ quic_frame_create_and_process_and_ack(path_response, 0),
+ quic_frame_create_and_process_and_ack(connection_close, 0),
+ quic_frame_create_and_process_and_ack(connection_close, 0),
+ quic_frame_create_and_process_and_ack(handshake_done, 1),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0), /* 0x20 */
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(invalid, 0),
+ quic_frame_create_and_process_and_ack(datagram, 1), /* 0x30 */
+ quic_frame_create_and_process_and_ack(datagram, 1),
+};
+
+void quic_frame_ack(struct sock *sk, struct quic_frame *frame)
+{
+ quic_frame_ops[frame->type].frame_ack(sk, frame);
+
+ list_del_init(&frame->list);
+ frame->transmitted = 0;
+ quic_frame_put(frame);
+}
+
+int quic_frame_process(struct sock *sk, struct quic_frame *frame)
+{
+ u8 type, level = frame->level;
+ int ret;
+
+ while (frame->len > 0) {
+ type = *frame->data++;
+ frame->len--;
+
+ if (type > QUIC_FRAME_MAX) {
+ pr_debug("%s: unsupported frame, type: %x, level: %d\n",
+ __func__, type, level);
+ return -EPROTONOSUPPORT;
+ } else if (quic_frame_level_check(level, type)) {
+ pr_debug("%s: invalid frame, type: %x, level: %d\n",
+ __func__, type, level);
+ return -EINVAL;
+ }
+ ret = quic_frame_ops[type].frame_process(sk, frame, type);
+ if (ret < 0) {
+ pr_debug("%s: failed, type: %x, level: %d, err: %d\n",
+ __func__, type, level, ret);
+ return ret;
+ }
+ pr_debug("%s: done, type: %x, level: %d\n", __func__, type, level);
+
+ frame->data += ret;
+ frame->len -= ret;
+ }
+ return 0;
+}
+
+struct quic_frame *quic_frame_create(struct sock *sk, u8 type, void *data)
+{
+ struct quic_frame *frame;
+
+ if (type > QUIC_FRAME_MAX)
+ return NULL;
+ frame = quic_frame_ops[type].frame_create(sk, data, type);
+ if (!frame) {
+ pr_debug("%s: failed, type: %x\n", __func__, type);
+ return NULL;
+ }
+ INIT_LIST_HEAD(&frame->list);
+ if (!frame->type)
+ frame->type = type;
+ frame->ack_eliciting = quic_frame_ops[type].ack_eliciting;
+ pr_debug("%s: done, type: %x, len: %u\n", __func__, type, frame->len);
+ return frame;
+}
+
+struct quic_frame *quic_frame_alloc(u32 size, u8 *data, gfp_t gfp)
+{
+ struct quic_frame *frame;
+
+ frame = kmem_cache_zalloc(quic_frame_cachep, gfp);
+ if (!frame)
+ return NULL;
+ if (data) {
+ frame->data = data;
+ goto out;
+ }
+ frame->data = kmalloc(size, gfp);
+ if (!frame->data) {
+ kmem_cache_free(quic_frame_cachep, frame);
+ return NULL;
+ }
+out:
+ refcount_set(&frame->refcnt, 1);
+ frame->offset = -1;
+ frame->len = (u16)size;
+ frame->size = frame->len;
+ return frame;
+}
+
+static void quic_frame_free(struct quic_frame *frame)
+{
+ struct quic_frame_frag *frag, *next;
+
+ if (!frame->type && frame->skb) { /* RX path frame with skb. */
+ kfree_skb(frame->skb);
+ goto out;
+ }
+
+ for (frag = frame->flist; frag; frag = next) {
+ next = frag->next;
+ kfree(frag);
+ }
+ kfree(frame->data);
+out:
+ kmem_cache_free(quic_frame_cachep, frame);
+}
+
+struct quic_frame *quic_frame_get(struct quic_frame *frame)
+{
+ refcount_inc(&frame->refcnt);
+ return frame;
+}
+
+void quic_frame_put(struct quic_frame *frame)
+{
+ if (refcount_dec_and_test(&frame->refcnt))
+ quic_frame_free(frame);
+}
+
+int quic_frame_stream_append(struct sock *sk, struct quic_frame *frame,
+ struct quic_msginfo *info, u8 pack)
+{
+ return -1;
+}@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_CLOSE_PHRASE_MAX_LEN 80
+
+#define QUIC_TOKEN_MAX_LEN 120
+
+#define QUIC_TICKET_MIN_LEN 64
+#define QUIC_TICKET_MAX_LEN 4096
+
+#define QUIC_FRAME_BUF_SMALL 20
+#define QUIC_FRAME_BUF_LARGE 100
+
+enum {
+ QUIC_FRAME_PADDING = 0x00,
+ QUIC_FRAME_PING = 0x01,
+ QUIC_FRAME_ACK = 0x02,
+ QUIC_FRAME_ACK_ECN = 0x03,
+ QUIC_FRAME_RESET_STREAM = 0x04,
+ QUIC_FRAME_STOP_SENDING = 0x05,
+ QUIC_FRAME_CRYPTO = 0x06,
+ QUIC_FRAME_NEW_TOKEN = 0x07,
+ QUIC_FRAME_STREAM = 0x08,
+ QUIC_FRAME_MAX_DATA = 0x10,
+ QUIC_FRAME_MAX_STREAM_DATA = 0x11,
+ QUIC_FRAME_MAX_STREAMS_BIDI = 0x12,
+ QUIC_FRAME_MAX_STREAMS_UNI = 0x13,
+ QUIC_FRAME_DATA_BLOCKED = 0x14,
+ QUIC_FRAME_STREAM_DATA_BLOCKED = 0x15,
+ QUIC_FRAME_STREAMS_BLOCKED_BIDI = 0x16,
+ QUIC_FRAME_STREAMS_BLOCKED_UNI = 0x17,
+ QUIC_FRAME_NEW_CONNECTION_ID = 0x18,
+ QUIC_FRAME_RETIRE_CONNECTION_ID = 0x19,
+ QUIC_FRAME_PATH_CHALLENGE = 0x1a,
+ QUIC_FRAME_PATH_RESPONSE = 0x1b,
+ QUIC_FRAME_CONNECTION_CLOSE = 0x1c,
+ QUIC_FRAME_CONNECTION_CLOSE_APP = 0x1d,
+ QUIC_FRAME_HANDSHAKE_DONE = 0x1e,
+ QUIC_FRAME_DATAGRAM = 0x30, /* RFC 9221 */
+ QUIC_FRAME_DATAGRAM_LEN = 0x31,
+ QUIC_FRAME_MAX = QUIC_FRAME_DATAGRAM_LEN,
+};
+
+enum {
+ QUIC_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000,
+ QUIC_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001,
+ QUIC_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002,
+ QUIC_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003,
+ QUIC_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004,
+ QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005,
+ QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006,
+ QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007,
+ QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008,
+ QUIC_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009,
+ QUIC_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a,
+ QUIC_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b,
+ QUIC_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c,
+ QUIC_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d,
+ QUIC_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e,
+ QUIC_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f,
+ QUIC_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010,
+ QUIC_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020,
+ QUIC_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2,
+ QUIC_TRANSPORT_PARAM_VERSION_INFORMATION = 0x11,
+ QUIC_TRANSPORT_PARAM_DISABLE_1RTT_ENCRYPTION = 0xbaad,
+};
+
+/* Arguments passed to create a STREAM frame */
+struct quic_msginfo {
+ struct quic_stream *stream; /* The QUIC stream associated with this frame */
+ struct iov_iter *msg; /* Iterator over message data to send */
+ u32 flags; /* Flags controlling stream frame creation */
+ u8 level; /* Encryption level for this frame */
+};
+
+/* Arguments passed to create a PING frame */
+struct quic_probeinfo {
+ u16 size; /* Size of the PING packet */
+ u8 level; /* Encryption level for this frame */
+};
+
+/* Operations for creating, processing, and acknowledging QUIC frames */
+struct quic_frame_ops {
+ struct quic_frame *(*frame_create)(struct sock *sk, void *data, u8 type);
+ int (*frame_process)(struct sock *sk, struct quic_frame *frame, u8 type);
+ void (*frame_ack)(struct sock *sk, struct quic_frame *frame);
+ u8 ack_eliciting;
+};
+
+/* Fragment of data appended to a STREAM frame */
+struct quic_frame_frag {
+ struct quic_frame_frag *next; /* Next fragment in the linked list */
+ u16 size; /* Size of this data fragment */
+ u8 data[]; /* Flexible array member holding fragment data */
+};
+
+struct quic_frame {
+ union {
+ struct quic_frame_frag *flist; /* For TX: linked list of appended data fragments */
+ struct sk_buff *skb; /* For RX: skb containing the raw frame data */
+ };
+ struct quic_stream *stream; /* Stream related to this frame, NULL if none */
+ struct list_head list; /* Linked list node for queuing frames */
+ s64 offset; /* Stream offset, crypto data offset, or first pkt number */
+ u8 *data; /* Pointer to the actual frame data buffer */
+
+ refcount_t refcnt;
+ u16 errcode; /* Error code set during frame processing */
+ u8 level; /* Packet number space: Initial, Handshake, or App */
+ u8 type; /* Frame type identifier */
+ u16 bytes; /* Number of user data bytes */
+ u16 size; /* Allocated data buffer size */
+ u16 len; /* Total frame length including appended fragments */
+
+ u8 ack_eliciting:1; /* Frame requires acknowledgment */
+ u8 transmitted:1; /* Frame is in the transmitted queue */
+ u8 stream_fin:1; /* Frame includes FIN flag for stream */
+ u8 nodelay:1; /* Frame bypasses Nagle's algorithm for sending */
+ u8 padding:1; /* Padding is needed after this frame */
+ u8 dgram:1; /* Frame represents a datagram message (RX only) */
+ u8 event:1; /* Frame represents an event (RX only) */
+ u8 path:1; /* Path index used to send this frame */
+};
+
+static inline bool quic_frame_new_conn_id(u8 type)
+{
+ return type == QUIC_FRAME_NEW_CONNECTION_ID;
+}
+
+static inline bool quic_frame_dgram(u8 type)
+{
+ return type == QUIC_FRAME_DATAGRAM || type == QUIC_FRAME_DATAGRAM_LEN;
+}
+
+static inline bool quic_frame_stream(u8 type)
+{
+ return type >= QUIC_FRAME_STREAM && type < QUIC_FRAME_MAX_DATA;
+}
+
+static inline bool quic_frame_sack(u8 type)
+{
+ return type == QUIC_FRAME_ACK || type == QUIC_FRAME_ACK_ECN;
+}
+
+static inline bool quic_frame_ping(u8 type)
+{
+ return type == QUIC_FRAME_PING;
+}
+
+/* Check if a given frame type is valid for the specified encryption level,
+ * based on the Frame Types table from rfc9000#section-12.4.
+ *
+ * Returns 0 if valid, 1 otherwise.
+ */
+static inline int quic_frame_level_check(u8 level, u8 type)
+{
+ if (level == QUIC_CRYPTO_APP)
+ return 0;
+
+ if (level == QUIC_CRYPTO_EARLY) {
+ if (type == QUIC_FRAME_ACK || type == QUIC_FRAME_ACK_ECN ||
+ type == QUIC_FRAME_CRYPTO || type == QUIC_FRAME_HANDSHAKE_DONE ||
+ type == QUIC_FRAME_NEW_TOKEN || type == QUIC_FRAME_PATH_RESPONSE ||
+ type == QUIC_FRAME_RETIRE_CONNECTION_ID)
+ return 1;
+ return 0;
+ }
+
+ if (type != QUIC_FRAME_ACK && type != QUIC_FRAME_ACK_ECN &&
+ type != QUIC_FRAME_PADDING && type != QUIC_FRAME_PING &&
+ type != QUIC_FRAME_CRYPTO && type != QUIC_FRAME_CONNECTION_CLOSE)
+ return 1;
+ return 0;
+}
+
+int quic_frame_stream_append(struct sock *sk, struct quic_frame *frame,
+ struct quic_msginfo *info, u8 pack);
+
+struct quic_frame *quic_frame_alloc(u32 size, u8 *data, gfp_t gfp);
+struct quic_frame *quic_frame_get(struct quic_frame *frame);
+void quic_frame_put(struct quic_frame *frame);
+
+struct quic_frame *quic_frame_create(struct sock *sk, u8 type, void *data);
+int quic_frame_process(struct sock *sk, struct quic_frame *frame);
+void quic_frame_ack(struct sock *sk, struct quic_frame *frame);