diff --git a/fw/hpack.h b/fw/hpack.h index 6e057344ca..4fd69fe5a9 100644 --- a/fw/hpack.h +++ b/fw/hpack.h @@ -91,7 +91,6 @@ typedef struct { * @window - maximum pseudo-length of the dynamic table (in bytes); this * value used as threshold to flushing old entries; * @wnd_changed - flag indicates, that window was changed by settings update; - * @ack_sent - flag indicates, that ack for window change was sent; * @rbuf - pointer to the ring buffer; * @root - pointer to the root node of binary tree; * @pool - memory pool for dynamic table; @@ -102,7 +101,6 @@ typedef struct { TFW_HPACK_ETBL_COMMON; unsigned short window; bool wnd_changed; - bool ack_sent; char *rbuf; TfwHPackNode *root; TfwPool *pool; diff --git a/fw/http_frame.c b/fw/http_frame.c index 58d00eb692..4d641c8df5 100644 --- a/fw/http_frame.c +++ b/fw/http_frame.c @@ -73,19 +73,6 @@ typedef enum { HTTP2_RECV_APP_DATA_POST } TfwFrameState; -/** - * IDs for SETTINGS parameters of HTTP/2 connection (RFC 7540 - * section 6.5.2). - */ -typedef enum { - HTTP2_SETTINGS_TABLE_SIZE = 0x01, - HTTP2_SETTINGS_ENABLE_PUSH, - HTTP2_SETTINGS_MAX_STREAMS, - HTTP2_SETTINGS_INIT_WND_SIZE, - HTTP2_SETTINGS_MAX_FRAME_SIZE, - HTTP2_SETTINGS_MAX_HDR_LIST_SIZE -} TfwSettingsId; - #define __FRAME_FSM_EXIT() \ do { \ ctx->rlen = 0; \ @@ -1211,25 +1198,22 @@ tfw_h2_apply_wnd_sz_change(TfwH2Ctx *ctx, long int delta) } } -static int +static void tfw_h2_apply_settings_entry(TfwH2Ctx *ctx, unsigned short id, unsigned int val) { - TfwH2Conn *conn = container_of(ctx, TfwH2Conn, h2); TfwSettings *dest = &ctx->rsettings; long int delta; switch (id) { case HTTP2_SETTINGS_TABLE_SIZE: - assert_spin_locked(&((TfwConn *)conn)->sk->sk_lock.slock); dest->hdr_tbl_sz = min_t(unsigned int, val, HPACK_ENC_TABLE_MAX_SIZE); tfw_hpack_set_rbuf_size(&ctx->hpack.enc_tbl, dest->hdr_tbl_sz); break; case HTTP2_SETTINGS_ENABLE_PUSH: - if (val > 1) - return -EINVAL; + BUG_ON(val > 1); dest->push = val; break; @@ -1238,17 +1222,14 @@ tfw_h2_apply_settings_entry(TfwH2Ctx *ctx, unsigned short id, break; case HTTP2_SETTINGS_INIT_WND_SIZE: - if (val > MAX_WND_SIZE) - return -EINVAL; - + BUG_ON(val > MAX_WND_SIZE); delta = (long int)val - (long int)dest->wnd_sz; tfw_h2_apply_wnd_sz_change(ctx, delta); dest->wnd_sz = val; break; case HTTP2_SETTINGS_MAX_FRAME_SIZE: - if (val < FRAME_DEF_LENGTH || val > FRAME_MAX_LENGTH) - return -EINVAL; + BUG_ON(val < FRAME_DEF_LENGTH || val > FRAME_MAX_LENGTH); dest->max_frame_sz = val; break; @@ -1261,12 +1242,97 @@ tfw_h2_apply_settings_entry(TfwH2Ctx *ctx, unsigned short id, * We should silently ignore unknown identifiers (see * RFC 7540 section 6.5.2) */ - return 0; + break; + } +} + +static int +tfw_h2_check_settings_entry(TfwH2Ctx *ctx, unsigned short id, unsigned int val) +{ + TfwH2Conn *conn = container_of(ctx, TfwH2Conn, h2); + + assert_spin_locked(&((TfwConn *)conn)->sk->sk_lock.slock); + + switch (id) { + case HTTP2_SETTINGS_TABLE_SIZE: + break; + + case HTTP2_SETTINGS_ENABLE_PUSH: + if (val > 1) + return -EINVAL; + break; + + case HTTP2_SETTINGS_MAX_STREAMS: + break; + + case HTTP2_SETTINGS_INIT_WND_SIZE: + if (val > MAX_WND_SIZE) + return -EINVAL; + break; + + case HTTP2_SETTINGS_MAX_FRAME_SIZE: + if (val < FRAME_DEF_LENGTH || val > FRAME_MAX_LENGTH) + return -EINVAL; + break; + + case HTTP2_SETTINGS_MAX_HDR_LIST_SIZE: + break; + + default: + /* + * We should silently ignore unknown identifiers (see + * RFC 7540 section 6.5.2) + */ + break; } return 0; } +/** + * Flags indicates that appropriate SETTINGS parameter is waited for + * update. + */ +static const unsigned char +ctx_new_settings_flags[] = { + [HTTP2_SETTINGS_TABLE_SIZE] = 0x01, + [HTTP2_SETTINGS_ENABLE_PUSH] = 0x02, + [HTTP2_SETTINGS_MAX_STREAMS] = 0x04, + [HTTP2_SETTINGS_INIT_WND_SIZE] = 0x08, + [HTTP2_SETTINGS_MAX_FRAME_SIZE] = 0x10, + [HTTP2_SETTINGS_MAX_HDR_LIST_SIZE] = 0x20 +}; + +static void +tfw_h2_save_settings_entry(TfwH2Ctx *ctx, unsigned short id, unsigned int val) +{ + TfwH2Conn *conn = container_of(ctx, TfwH2Conn, h2); + + assert_spin_locked(&((TfwConn *)conn)->sk->sk_lock.slock); + + if (id < _HTTP2_SETTINGS_MAX) { + ctx->new_settings.settings[id] = val; + ctx->new_settings.flags |= ctx_new_settings_flags[id]; + } +} + +void +tfw_h2_apply_new_settings(TfwH2Ctx *ctx) +{ + TfwH2Conn *conn = container_of(ctx, TfwH2Conn, h2); + unsigned int id; + + assert_spin_locked(&((TfwConn *)conn)->sk->sk_lock.slock); + + for (id = HTTP2_SETTINGS_TABLE_SIZE; id < _HTTP2_SETTINGS_MAX; id++) { + if (ctx->new_settings.flags & ctx_new_settings_flags[id]) { + unsigned int val = ctx->new_settings.settings[id]; + tfw_h2_apply_settings_entry(ctx, id, val); + } + } + ctx->new_settings.flags = 0; +} + static void tfw_h2_settings_ack_process(TfwH2Ctx *ctx) { @@ -1289,9 +1355,11 @@ tfw_h2_settings_process(TfwH2Ctx *ctx) T_DBG3("%s: entry parsed, id=%hu, val=%u\n", __func__, id, val); - if ((r = tfw_h2_apply_settings_entry(ctx, id, val))) + if ((r = tfw_h2_check_settings_entry(ctx, id, val))) return r; + tfw_h2_save_settings_entry(ctx, id, val); + ctx->to_read = hdr->length ? FRAME_SETTINGS_ENTRY_SIZE : 0; hdr->length -= ctx->to_read; @@ -2515,7 +2583,7 @@ do { \ T_FSM_STATE(HTTP2_MAKE_HEADERS_FRAMES) { ADJUST_AVAILABLE_CWND(tmp_cwnd_awail, HTTP2_HEADERS); - if (unlikely(ctx->hpack.enc_tbl.ack_sent)) { + if (unlikely(ctx->hpack.enc_tbl.wnd_changed)) { r = tfw_hpack_enc_tbl_write_sz(&ctx->hpack.enc_tbl, stream); if (unlikely(r < 0)) { @@ -2523,7 +2591,6 @@ do { \ "table size %d", r); return r; } - ctx->hpack.enc_tbl.ack_sent = false; } r = tfw_h2_insert_frame_header_and_send(ctx, stream, frame_type, diff --git a/fw/http_frame.h b/fw/http_frame.h index fa72f6cf86..bda76544cb 100644 --- a/fw/http_frame.h +++ b/fw/http_frame.h @@ -48,6 +48,20 @@ typedef enum { _HTTP2_UNDEFINED } TfwFrameType; +/** + * IDs for SETTINGS parameters of HTTP/2 connection (RFC 7540 + * section 6.5.2). + */ +typedef enum { + HTTP2_SETTINGS_TABLE_SIZE = 0x01, + HTTP2_SETTINGS_ENABLE_PUSH, + HTTP2_SETTINGS_MAX_STREAMS, + HTTP2_SETTINGS_INIT_WND_SIZE, + HTTP2_SETTINGS_MAX_FRAME_SIZE, + HTTP2_SETTINGS_MAX_HDR_LIST_SIZE, + _HTTP2_SETTINGS_MAX, +} TfwSettingsId; + static const char *__tfw_h2_frm_names[] = { [HTTP2_DATA] = "DATA", [HTTP2_HEADERS] = "HEADERS", @@ -156,6 +170,8 @@ typedef struct { * @rem_wnd - remote peer current flow controlled window; * @hpack - HPACK context, used in processing of * HEADERS/CONTINUATION frames; + * @new_settings - new settings to apply when ack is pushed to socket + * write queue; * @__off - offset to reinitialize processing context; * @skb_head - collected list of processed skbs containing HTTP/2 * frames; @@ -195,6 +211,10 @@ typedef struct { long int loc_wnd; long int rem_wnd; TfwHPack hpack; + struct { + unsigned int settings[_HTTP2_SETTINGS_MAX]; + unsigned char flags; + } new_settings; char __off[0]; struct sk_buff *skb_head; TfwStream *cur_stream; @@ -217,6 +237,7 @@ int tfw_h2_context_init(TfwH2Ctx *ctx); void tfw_h2_context_clear(TfwH2Ctx *ctx); int tfw_h2_frame_process(TfwConn *c, struct sk_buff *skb, struct sk_buff **next); +void tfw_h2_apply_new_settings(TfwH2Ctx *ctx); void tfw_h2_conn_streams_cleanup(TfwH2Ctx *ctx); TfwStream *tfw_h2_find_not_closed_stream(TfwH2Ctx *ctx, unsigned int id, bool recv); diff --git a/fw/sock.c b/fw/sock.c index fef51bd06a..be0936b988 100644 --- a/fw/sock.c +++ b/fw/sock.c @@ -445,17 +445,8 @@ ss_do_send(struct sock *sk, struct sk_buff **skb_head, int flags) } if (!stream) { - if (h2 && unlikely(h2->hpack.enc_tbl.wnd_changed && - !h2->hpack.enc_tbl.ack_sent)) - { - TfwFrameHdr hdr; - char *data = ss_skb_data_ptr_by_offset(skb, 0); - - tfw_h2_unpack_frame_header(&hdr, data); - if (hdr.type == HTTP2_SETTINGS && - hdr.flags == HTTP2_F_ACK) - h2->hpack.enc_tbl.ack_sent = true; - } + if (h2 && unlikely(h2->new_settings.flags)) + tfw_h2_apply_new_settings(h2); ss_skb_entail(sk, skb, mark, tls_type); } else { ss_skb_queue_tail(&stream->xmit.skb_head, skb);