diff options
author | 2025-02-28 10:15:15 -0500 | |
---|---|---|
committer | 2025-03-03 16:07:10 -0500 | |
commit | 86abfc3e0aee3a1a1ae9107d03d98bf1cdcedd33 (patch) | |
tree | 7cacb611468126fa8d45b17668b1a5f8b0fe723e /src | |
parent | 193371ef11e3ed95a095e9d4169c22b18fb94af1 (diff) |
http: http2 frames
Type: feature
Change-Id: Id2c6f6b5747c1f676048642e277eb66850f728b7
Signed-off-by: Matus Fabian <matfabia@cisco.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/http/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/plugins/http/http2/frame.c | 323 | ||||
-rw-r--r-- | src/plugins/http/http2/frame.h | 235 | ||||
-rw-r--r-- | src/plugins/http/http2/http2.h | 39 | ||||
-rw-r--r-- | src/plugins/http/test/http_test.c | 284 |
5 files changed, 882 insertions, 0 deletions
diff --git a/src/plugins/http/CMakeLists.txt b/src/plugins/http/CMakeLists.txt index 95b5102684a..58cb4c000e3 100644 --- a/src/plugins/http/CMakeLists.txt +++ b/src/plugins/http/CMakeLists.txt @@ -14,6 +14,7 @@ add_vpp_plugin(http SOURCES http2/hpack.c + http2/frame.c http.c http_buffer.c http_timer.c diff --git a/src/plugins/http/http2/frame.c b/src/plugins/http/http2/frame.c new file mode 100644 index 00000000000..577bb6c1e3b --- /dev/null +++ b/src/plugins/http/http2/frame.c @@ -0,0 +1,323 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#include <vppinfra/string.h> +#include <http/http2/frame.h> + +#define MAX_U24 0xFFFFFF + +static_always_inline u8 * +http2_decode_u24 (u8 *src, u32 *value) +{ + *value = 0; + *value = (u32) (src[0] << 16) | (u32) (src[1] << 8) | (u32) src[2]; + return src + 3; +} + +static_always_inline u8 * +http2_encode_u24 (u8 *dst, u32 value) +{ + ASSERT (value <= MAX_U24); + *dst++ = (value >> 16) & 0xFF; + *dst++ = (value >> 8) & 0xFF; + *dst++ = value & 0xFF; + return dst; +} + +/* + * RFC9113 section 4.1 + * + * HTTP Frame { + * Length (24), + * Type (8), + * Flags (8), + * Reserved (1), + * Stream Identifier (31), + * Frame Payload (..), + * } + */ + +__clib_export void +http2_frame_header_read (u8 *src, http2_frame_header_t *fh) +{ + u32 *stream_id; + src = http2_decode_u24 (src, &fh->length); + fh->type = *src++; + fh->flags = *src++; + stream_id = (u32 *) src; + fh->stream_id = clib_net_to_host_u32 (*stream_id) & 0x7FFFFFFF; +} + +static void +http2_frame_header_write (http2_frame_header_t *fh, u8 *dst) +{ + u32 stream_id; + + dst = http2_encode_u24 (dst, fh->length); + *dst++ = fh->type; + *dst++ = fh->flags; + stream_id = clib_host_to_net_u32 (fh->stream_id); + clib_memcpy_fast (dst, &stream_id, sizeof (stream_id)); +} + +__clib_export http2_error_t +http2_frame_read_settings (http2_conn_settings_t *settings, u8 *payload, + u32 payload_len) +{ + http2_settings_entry_t *entry; + u32 value; + + while (payload_len >= sizeof (*entry)) + { + entry = (http2_settings_entry_t *) payload; + switch (clib_net_to_host_u16 (entry->identifier)) + { +#define _(v, label, member, min, max, default_value, err_code) \ + case HTTP2_SETTINGS_##label: \ + value = clib_net_to_host_u32 (entry->value); \ + if (!(value >= min && value <= max)) \ + return err_code; \ + settings->member = value; \ + break; + foreach_http2_settings +#undef _ + /* ignore unknown or unsupported identifier */ + default : break; + } + payload_len -= sizeof (*entry); + payload += sizeof (*entry); + } + + if (payload_len != 0) + return HTTP2_ERROR_FRAME_SIZE_ERROR; + + return HTTP2_ERROR_NO_ERROR; +} + +__clib_export void +http2_frame_write_settings_ack (u8 **dst) +{ + http2_frame_header_t fh = { .flags = HTTP2_FRAME_FLAG_ACK, + .type = HTTP2_FRAME_TYPE_SETTINGS }; + u8 *p = http2_frame_header_alloc (dst); + http2_frame_header_write (&fh, p); +} + +__clib_export void +http2_frame_write_settings (http2_settings_entry_t *settings, u8 **dst) +{ + u8 *p; + u32 length; + http2_settings_entry_t *entry, e; + + ASSERT (settings); + ASSERT (vec_len (settings) > 0); + + length = vec_len (settings) * sizeof (*entry); + http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_SETTINGS, + .length = length }; + p = http2_frame_header_alloc (dst); + http2_frame_header_write (&fh, p); + + vec_add2 (*dst, p, length); + vec_foreach (entry, settings) + { + e.identifier = clib_host_to_net_u16 (entry->identifier); + e.value = clib_host_to_net_u32 (entry->value); + clib_memcpy_fast (p, &e, sizeof (e)); + p += sizeof (e); + } +} + +#define WINDOW_UPDATE_LENGTH 4 + +__clib_export http2_error_t +http2_frame_read_window_update (u32 *increment, u8 *payload, u32 payload_len) +{ + u32 *value; + + if (payload_len != WINDOW_UPDATE_LENGTH) + return HTTP2_ERROR_FRAME_SIZE_ERROR; + + value = (u32 *) payload; + + if (value == 0) + return HTTP2_ERROR_PROTOCOL_ERROR; + + *increment = clib_net_to_host_u32 (*value) & 0x7FFFFFFF; + return HTTP2_ERROR_NO_ERROR; +} + +__clib_export void +http2_frame_write_window_update (u32 increment, u32 stream_id, u8 **dst) +{ + u8 *p; + u32 value; + + ASSERT (increment > 0 && increment <= 0x7FFFFFFF); + + http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_WINDOW_UPDATE, + .length = WINDOW_UPDATE_LENGTH, + .stream_id = stream_id }; + p = http2_frame_header_alloc (dst); + http2_frame_header_write (&fh, p); + + vec_add2 (*dst, p, WINDOW_UPDATE_LENGTH); + value = clib_host_to_net_u32 (increment); + clib_memcpy_fast (p, &value, WINDOW_UPDATE_LENGTH); +} + +#define RST_STREAM_LENGTH 4 + +__clib_export http2_error_t +http2_frame_read_rst_stream (u32 *error_code, u8 *payload, u32 payload_len) +{ + u32 *value; + + if (payload_len != RST_STREAM_LENGTH) + return HTTP2_ERROR_FRAME_SIZE_ERROR; + + value = (u32 *) payload; + + *error_code = clib_net_to_host_u32 (*value); + return HTTP2_ERROR_NO_ERROR; +} + +__clib_export void +http2_frame_write_rst_stream (http2_error_t error_code, u32 stream_id, + u8 **dst) +{ + u8 *p; + u32 value; + + ASSERT (stream_id > 0 && stream_id <= 0x7FFFFFFF); + + http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_RST_STREAM, + .length = RST_STREAM_LENGTH, + .stream_id = stream_id }; + p = http2_frame_header_alloc (dst); + http2_frame_header_write (&fh, p); + + vec_add2 (*dst, p, RST_STREAM_LENGTH); + value = clib_host_to_net_u32 ((u32) error_code); + clib_memcpy_fast (p, &value, RST_STREAM_LENGTH); +} + +#define GOAWAY_MIN_SIZE 8 + +__clib_export http2_error_t +http2_frame_read_goaway (u32 *error_code, u32 *last_stream_id, u8 *payload, + u32 payload_len) +{ + u32 *value; + + if (payload_len < GOAWAY_MIN_SIZE) + return HTTP2_ERROR_FRAME_SIZE_ERROR; + + value = (u32 *) payload; + *last_stream_id = clib_net_to_host_u32 (*value) & 0x7FFFFFFF; + payload += 4; + + value = (u32 *) payload; + *error_code = clib_net_to_host_u32 (*value); + + /* TODO: Additional Debug Data */ + return HTTP2_ERROR_NO_ERROR; +} + +__clib_export void +http2_frame_write_goaway (http2_error_t error_code, u32 last_stream_id, + u8 **dst) +{ + u8 *p; + u32 value; + + ASSERT (last_stream_id > 0 && last_stream_id <= 0x7FFFFFFF); + + http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_GOAWAY, + .length = GOAWAY_MIN_SIZE }; + p = http2_frame_header_alloc (dst); + http2_frame_header_write (&fh, p); + + vec_add2 (*dst, p, GOAWAY_MIN_SIZE); + value = clib_host_to_net_u32 (last_stream_id); + clib_memcpy_fast (p, &value, 4); + p += 4; + value = clib_host_to_net_u32 ((u32) error_code); + clib_memcpy_fast (p, &value, 4); + /* TODO: Additional Debug Data */ +} + +#define PRIORITY_DATA_LEN 5 + +__clib_export http2_error_t +http2_frame_read_headers (u8 **headers, u32 *headers_len, u8 *payload, + u32 payload_len, u8 flags) +{ + *headers_len = payload_len; + + if (flags & HTTP2_FRAME_FLAG_PADED) + { + u8 pad_len = *payload++; + if ((u32) pad_len >= payload_len) + return HTTP2_ERROR_PROTOCOL_ERROR; + *headers_len -= pad_len; + } + + if (flags & HTTP2_FRAME_FLAG_PRIORITY) + { + if (*headers_len <= PRIORITY_DATA_LEN) + return HTTP2_ERROR_FRAME_SIZE_ERROR; + /* just skip, priority scheme defined in RFC7540 is deprecated */ + *headers_len -= PRIORITY_DATA_LEN; + payload += PRIORITY_DATA_LEN; + } + + *headers = payload; + + return HTTP2_ERROR_NO_ERROR; +} + +__clib_export void +http2_frame_write_headers_header (u32 headers_len, u32 stream_id, u8 flags, + u8 *dst) +{ + ASSERT (stream_id > 0 && stream_id <= 0x7FFFFFFF); + + http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_HEADERS, + .length = headers_len, + .flags = flags, + .stream_id = stream_id }; + http2_frame_header_write (&fh, dst); +} + +__clib_export http2_error_t +http2_frame_read_data (u8 **data, u32 *data_len, u8 *payload, u32 payload_len, + u8 flags) +{ + *data_len = payload_len; + + if (flags & HTTP2_FRAME_FLAG_PADED) + { + u8 pad_len = *payload++; + if ((u32) pad_len >= payload_len) + return HTTP2_ERROR_PROTOCOL_ERROR; + *data_len -= pad_len; + } + + *data = payload; + return HTTP2_ERROR_NO_ERROR; +} + +__clib_export void +http2_frame_write_data_header (u32 data_len, u32 stream_id, u8 flags, u8 *dst) +{ + ASSERT (stream_id > 0 && stream_id <= 0x7FFFFFFF); + + http2_frame_header_t fh = { .type = HTTP2_FRAME_TYPE_DATA, + .length = data_len, + .flags = flags, + .stream_id = stream_id }; + http2_frame_header_write (&fh, dst); +} diff --git a/src/plugins/http/http2/frame.h b/src/plugins/http/http2/frame.h new file mode 100644 index 00000000000..bfe4e122f0d --- /dev/null +++ b/src/plugins/http/http2/frame.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright(c) 2025 Cisco Systems, Inc. + */ + +#ifndef SRC_PLUGINS_HTTP_HTTP2_FRAME_H_ +#define SRC_PLUGINS_HTTP_HTTP2_FRAME_H_ + +#include <vppinfra/error.h> +#include <vppinfra/types.h> +#include <http/http2/http2.h> + +#define HTTP2_FRAME_HEADER_SIZE 9 + +#define foreach_http2_frame_type \ + _ (0x00, DATA, "DATA") \ + _ (0x01, HEADERS, "HEADERS") \ + _ (0x02, PRIORITY, "PRIORITY") \ + _ (0x03, RST_STREAM, "RST_STREAM") \ + _ (0x04, SETTINGS, "SETTINGS") \ + _ (0x05, PUSH_PROMISE, "PUSH_PROMISE") \ + _ (0x06, PING, "PING") \ + _ (0x07, GOAWAY, "GOAWAY") \ + _ (0x08, WINDOW_UPDATE, "WINDOW_UPDATE") \ + _ (0x09, CONTINUATION, "CONTINUATION") + +typedef enum +{ +#define _(v, n, s) HTTP2_FRAME_TYPE_##n = v, + foreach_http2_frame_type +#undef _ +} __clib_packed http2_frame_type_t; + +STATIC_ASSERT_SIZEOF (http2_frame_type_t, 1); + +#define foreach_http2_frame_flag \ + _ (0, NONE) \ + _ (1, END_STREAM) \ + _ (1, ACK) \ + _ (1 << 2, END_HEADERS) \ + _ (1 << 3, PADED) \ + _ (1 << 5, PRIORITY) + +typedef enum +{ +#define _(v, n) HTTP2_FRAME_FLAG_##n = v, + foreach_http2_frame_flag +#undef _ +} __clib_packed http2_frame_flag_t; + +STATIC_ASSERT_SIZEOF (http2_frame_flag_t, 1); + +typedef struct +{ + u32 length; + http2_frame_type_t type; + u8 flags; + u32 stream_id; +} http2_frame_header_t; + +typedef struct +{ + u16 identifier; + u32 value; +} __clib_packed http2_settings_entry_t; + +/** + * Parse frame header + * + * @param src Pointer to the beginning of the frame + * @param fh Parsed frame header + */ +void http2_frame_header_read (u8 *src, http2_frame_header_t *fh); + +/** + * Add 9 bytes (frame header size) to the end of given vector + * + * @param dst Pointer to vector + * + * @return Pointer to the frame header beginning + */ +static_always_inline u8 * +http2_frame_header_alloc (u8 **dst) +{ + u8 *p; + + vec_add2 (*dst, p, HTTP2_FRAME_HEADER_SIZE); + return p; +} + +/** + * Parse SETTINGS frame payload + * + * @param settings Vector of HTTP/2 settings + * @param payload Payload to parse + * @param payload_len Payload length + * + * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise + */ +http2_error_t http2_frame_read_settings (http2_conn_settings_t *settings, + u8 *payload, u32 payload_len); + +/** + * Write SETTINGS ACK frame to the end of given vector + * + * @param dst Vector where SETTINGS ACK frame will be written + */ +void http2_frame_write_settings_ack (u8 **dst); + +/** + * Write SETTINGS frame to the end of given vector + * + * @param settings Vector of HTTP/2 settings + * @param dst Vector where SETTINGS frame will be written + */ +void http2_frame_write_settings (http2_settings_entry_t *settings, u8 **dst); + +/** + * Parse WINDOW_UPDATE frame payload + * + * @param increment Parsed window increment value + * @param payload Payload to parse + * @param payload_len Payload length + * + * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise + */ +http2_error_t http2_frame_read_window_update (u32 *increment, u8 *payload, + u32 payload_len); + +/** + * Write WINDOW_UPDATE frame to the end of given vector + * + * @param increment Window increment value + * @param stream_id Stream ID + * @param dst Vector where WINDOW_UPDATE frame will be written + */ +void http2_frame_write_window_update (u32 increment, u32 stream_id, u8 **dst); + +/** + * Parse RST_STREAM frame payload + * + * @param error_code Parsed error code + * @param payload Payload to parse + * @param payload_len Payload length + * + * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise + */ +http2_error_t http2_frame_read_rst_stream (u32 *error_code, u8 *payload, + u32 payload_len); + +/** + * Write RST_STREAM frame to the end of given vector + * + * @param error_code Error code + * @param stream_id Stream ID, except 0 + * @param dst Vector where RST_STREAM frame will be written + */ +void http2_frame_write_rst_stream (http2_error_t error_code, u32 stream_id, + u8 **dst); + +/** + * Parse GOAWAY frame payload + * + * @param last_stream_id Parsed last stream ID + * @param error_code Parsed error code + * @param payload Payload to parse + * @param payload_len Payload length + * + * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise + */ +http2_error_t http2_frame_read_goaway (u32 *last_stream_id, u32 *error_code, + u8 *payload, u32 payload_len); + +/** + * Write GOAWAY frame to the end of given vector + * @param error_code Error code + * @param last_stream_id Last stream ID + * @param dst Vector where GOAWAY frame will be written + */ +void http2_frame_write_goaway (http2_error_t error_code, u32 last_stream_id, + u8 **dst); + +/** + * Parse HEADERS frame payload + * + * @param headers Pointer to header block fragment + * @param headers_len Header block fragment length + * @param payload Payload to parse + * @param payload_len Payload length + * @param flags Flag field of frame header + * + * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise + */ +http2_error_t http2_frame_read_headers (u8 **headers, u32 *headers_len, + u8 *payload, u32 payload_len, + u8 flags); + +/** + * Write HEADERS frame header + * + * @param headers_len Header block fragment length + * @param stream_id Stream ID, except 0 + * @param flags Frame header flags + * @param dst Pointer where frame header will be written + * + * @note Use @c http2_frame_header_alloc before + */ +void http2_frame_write_headers_header (u32 headers_len, u32 stream_id, + u8 flags, u8 *dst); + +/** + * Parse DATA frame payload + * + * @param headers Pointer to data + * @param headers_len Data length + * @param payload Payload to parse + * @param payload_len Payload length + * @param flags Flag field of frame header + * + * @return @c HTTP2_ERROR_NO_ERROR on success, error otherwise + */ +http2_error_t http2_frame_read_data (u8 **data, u32 *data_len, u8 *payload, + u32 payload_len, u8 flags); + +/** + * Write DATA frame header + * + * @param data_len Data length + * @param stream_id Stream ID, except 0 + * @param flags Frame header flags + * @param dst Pointer where frame header will be written + */ +void http2_frame_write_data_header (u32 data_len, u32 stream_id, u8 flags, + u8 *dst); + +#endif /* SRC_PLUGINS_HTTP_HTTP2_FRAME_H_ */ diff --git a/src/plugins/http/http2/http2.h b/src/plugins/http/http2/http2.h index 82403a46fc3..9fc95344771 100644 --- a/src/plugins/http/http2/http2.h +++ b/src/plugins/http/http2/http2.h @@ -5,6 +5,9 @@ #ifndef SRC_PLUGINS_HTTP_HTTP2_H_ #define SRC_PLUGINS_HTTP_HTTP2_H_ +#include <vppinfra/format.h> +#include <vppinfra/types.h> + /* RFC9113 section 7 */ #define foreach_http2_error \ _ (NO_ERROR, "NO_ERROR") \ @@ -55,4 +58,40 @@ format_http2_error (u8 *s, va_list *va) _ (3, PATH, "path") \ _ (4, STATUS, "status") +/* value, label, member, min, max, default_value, err_code */ +#define foreach_http2_settings \ + _ (1, HEADER_TABLE_SIZE, header_table_size, 0, CLIB_U32_MAX, 4096, \ + HTTP2_ERROR_NO_ERROR) \ + _ (2, ENABLE_PUSH, enable_push, 0, 1, 1, HTTP2_ERROR_PROTOCOL_ERROR) \ + _ (3, MAX_CONCURRENT_STREAMS, max_concurrent_streams, 0, CLIB_U32_MAX, \ + CLIB_U32_MAX, HTTP2_ERROR_NO_ERROR) \ + _ (4, INITIAL_WINDOW_SIZE, initial_window_size, 0, 0x7FFFFFFF, 65535, \ + HTTP2_ERROR_FLOW_CONTROL_ERROR) \ + _ (5, MAX_FRAME_SIZE, max_frame_size, 16384, 16777215, 16384, \ + HTTP2_ERROR_PROTOCOL_ERROR) \ + _ (6, MAX_HEADER_LIST_SIZE, max_header_list_size, 0, CLIB_U32_MAX, \ + CLIB_U32_MAX, HTTP2_ERROR_NO_ERROR) + +typedef enum +{ +#define _(value, label, member, min, max, default_value, err_code) \ + HTTP2_SETTINGS_##label = value, + foreach_http2_settings +#undef _ +} http_settings_t; + +typedef struct +{ +#define _(value, label, member, min, max, default_value, err_code) u32 member; + foreach_http2_settings +#undef _ +} http2_conn_settings_t; + +static const http2_conn_settings_t http2_default_conn_settings = { +#define _(value, label, member, min, max, default_value, err_code) \ + default_value, + foreach_http2_settings +#undef _ +}; + #endif /* SRC_PLUGINS_HTTP_HTTP2_H_ */ diff --git a/src/plugins/http/test/http_test.c b/src/plugins/http/test/http_test.c index 691efdf6138..f44d3cbd31b 100644 --- a/src/plugins/http/test/http_test.c +++ b/src/plugins/http/test/http_test.c @@ -7,6 +7,7 @@ #include <http/http.h> #include <http/http_header_names.h> #include <http/http2/hpack.h> +#include <http/http2/frame.h> #define HTTP_TEST_I(_cond, _comment, _args...) \ ({ \ @@ -1020,6 +1021,285 @@ http_test_hpack (vlib_main_t *vm) return 0; } +static int +http_test_h2_frame (vlib_main_t *vm) +{ + static void (*_http2_frame_header_read) (u8 * src, + http2_frame_header_t * fh); + + _http2_frame_header_read = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_header_read"); + + vlib_cli_output (vm, "http2_frame_read_settings"); + + static http2_error_t (*_http2_frame_read_settings) ( + http2_conn_settings_t * settings, u8 * payload, u32 payload_len); + + _http2_frame_read_settings = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_settings"); + + http2_error_t rv; + http2_frame_header_t fh = { 0 }; + http2_conn_settings_t conn_settings = http2_default_conn_settings; + + u8 settings[] = { 0x0, 0x0, 0x12, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x3, 0x0, 0x0, 0x0, 0x64, 0x0, 0x4, 0x40, + 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0 }; + _http2_frame_header_read (settings, &fh); + HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_SETTINGS && + fh.stream_id == 0 && fh.length == 18), + "frame identified as SETTINGS"); + + rv = _http2_frame_read_settings ( + &conn_settings, settings + HTTP2_FRAME_HEADER_SIZE, fh.length); + HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && + conn_settings.max_concurrent_streams == 100 && + conn_settings.initial_window_size == 1073741824 && + conn_settings.enable_push == 0), + "SETTINGS frame payload parsed") + + u8 settings_ack[] = { 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0 }; + _http2_frame_header_read (settings_ack, &fh); + HTTP_TEST ((fh.flags == HTTP2_FRAME_FLAG_ACK && + fh.type == HTTP2_FRAME_TYPE_SETTINGS && fh.stream_id == 0 && + fh.length == 0), + "frame identified as SETTINGS ACK"); + + vlib_cli_output (vm, "http2_frame_write_settings_ack"); + + static void (*_http2_frame_write_settings_ack) (u8 * *dst); + + _http2_frame_write_settings_ack = vlib_get_plugin_symbol ( + "http_plugin.so", "http2_frame_write_settings_ack"); + + u8 *buf = 0; + + _http2_frame_write_settings_ack (&buf); + HTTP_TEST ((vec_len (buf) == sizeof (settings_ack)) && + !memcmp (buf, settings_ack, sizeof (settings_ack)), + "SETTINGS ACK frame written"); + vec_free (buf); + + vlib_cli_output (vm, "http2_frame_write_settings"); + + static void (*_http2_frame_write_settings) ( + http2_settings_entry_t * settings, u8 * *dst); + + _http2_frame_write_settings = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_settings"); + + http2_settings_entry_t *settings_list = 0; + vec_validate (settings_list, 2); + settings_list[0].identifier = HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; + settings_list[0].value = 100; + settings_list[1].identifier = HTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + settings_list[1].value = 1073741824; + settings_list[2].identifier = HTTP2_SETTINGS_ENABLE_PUSH; + settings_list[2].value = 0; + + _http2_frame_write_settings (settings_list, &buf); + HTTP_TEST ((vec_len (buf) == sizeof (settings) && + !memcmp (buf, settings, sizeof (settings))), + "SETTINGS frame written"); + vec_free (settings_list); + vec_free (buf); + + vlib_cli_output (vm, "http2_frame_read_window_update"); + + static http2_error_t (*_http2_frame_read_window_update) ( + u32 * increment, u8 * payload, u32 payload_len); + + _http2_frame_read_window_update = vlib_get_plugin_symbol ( + "http_plugin.so", "http2_frame_read_window_update"); + + u32 win_increment; + u8 win_update[] = { 0x0, 0x0, 0x4, 0x8, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3f, 0xff, 0x0, 0x1 }; + _http2_frame_header_read (win_update, &fh); + HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_WINDOW_UPDATE && + fh.stream_id == 0 && fh.length == 4), + "frame identified as WINDOW_UPDATE"); + + rv = _http2_frame_read_window_update ( + &win_increment, win_update + HTTP2_FRAME_HEADER_SIZE, fh.length); + HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && win_increment == 1073676289), + "WINDOW_UPDATE frame payload parsed") + + vlib_cli_output (vm, "http2_frame_write_window_update"); + + static void (*_http2_frame_write_window_update) (u32 increment, + u32 stream_id, u8 * *dst); + + _http2_frame_write_window_update = vlib_get_plugin_symbol ( + "http_plugin.so", "http2_frame_write_window_update"); + + _http2_frame_write_window_update (1073676289, 0, &buf); + HTTP_TEST ((vec_len (buf) == sizeof (win_update) && + !memcmp (buf, win_update, sizeof (win_update))), + "WINDOW_UPDATE frame written"); + vec_free (buf); + + vlib_cli_output (vm, "http2_frame_read_rst_stream"); + + static http2_error_t (*_http2_frame_read_rst_stream) ( + u32 * error_code, u8 * payload, u32 payload_len); + + _http2_frame_read_rst_stream = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_rst_stream"); + + u32 error_code; + u8 rst_stream[] = { 0x0, 0x0, 0x4, 0x3, 0x0, 0x0, 0x0, + 0x0, 0x5, 0x0, 0x0, 0x0, 0x01 }; + _http2_frame_header_read (rst_stream, &fh); + HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_RST_STREAM && + fh.stream_id == 5 && fh.length == 4), + "frame identified as RST_STREAM"); + + rv = _http2_frame_read_rst_stream ( + &error_code, rst_stream + HTTP2_FRAME_HEADER_SIZE, fh.length); + HTTP_TEST ( + (rv == HTTP2_ERROR_NO_ERROR && error_code == HTTP2_ERROR_PROTOCOL_ERROR), + "RST_STREAM frame payload parsed") + + vlib_cli_output (vm, "http2_frame_write_rst_stream"); + + static void (*_http2_frame_write_rst_stream) (u32 increment, u32 stream_id, + u8 * *dst); + + _http2_frame_write_rst_stream = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_rst_stream"); + + _http2_frame_write_rst_stream (HTTP2_ERROR_PROTOCOL_ERROR, 5, &buf); + HTTP_TEST ((vec_len (buf) == sizeof (rst_stream) && + !memcmp (buf, rst_stream, sizeof (rst_stream))), + "RST_STREAM frame written"); + vec_free (buf); + + vlib_cli_output (vm, "http2_frame_read_goaway"); + + static http2_error_t (*_http2_frame_read_goaway) ( + u32 * error_code, u32 * last_stream_id, u8 * payload, u32 payload_len); + + _http2_frame_read_goaway = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_goaway"); + + u32 last_stream_id; + u8 goaway[] = { 0x0, 0x0, 0x8, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x2 }; + + _http2_frame_header_read (goaway, &fh); + HTTP_TEST ((fh.flags == 0 && fh.type == HTTP2_FRAME_TYPE_GOAWAY && + fh.stream_id == 0 && fh.length == 8), + "frame identified as GOAWAY"); + + rv = _http2_frame_read_goaway (&error_code, &last_stream_id, + goaway + HTTP2_FRAME_HEADER_SIZE, fh.length); + HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && + error_code == HTTP2_ERROR_INTERNAL_ERROR && last_stream_id == 5), + "GOAWAY frame payload parsed") + + vlib_cli_output (vm, "http2_frame_write_goaway"); + + static void (*_http2_frame_write_goaway) (http2_error_t error_code, + u32 last_stream_id, u8 * *dst); + + _http2_frame_write_goaway = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_goaway"); + + _http2_frame_write_goaway (HTTP2_ERROR_INTERNAL_ERROR, 5, &buf); + HTTP_TEST ((vec_len (buf) == sizeof (goaway) && + !memcmp (buf, goaway, sizeof (goaway))), + "GOAWAY frame written"); + vec_free (buf); + + vlib_cli_output (vm, "http2_frame_read_headers"); + + static http2_error_t (*_http2_frame_read_headers) ( + u8 * *headers, u32 * headers_len, u8 * payload, u32 payload_len, u8 flags); + + _http2_frame_read_headers = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_headers"); + + u8 *h; + u32 h_len; + u8 headers[] = { 0x0, 0x0, 0x28, 0x1, 0x5, 0x0, 0x0, 0x0, 0x3, 0x3f, + 0xe1, 0x1f, 0x82, 0x4, 0x88, 0x62, 0x7b, 0x69, 0x1d, 0x48, + 0x5d, 0x3e, 0x53, 0x86, 0x41, 0x88, 0xaa, 0x69, 0xd2, 0x9a, + 0xc4, 0xb9, 0xec, 0x9b, 0x7a, 0x88, 0x25, 0xb6, 0x50, 0xc3, + 0xab, 0xb8, 0x15, 0xc1, 0x53, 0x3, 0x2a, 0x2f, 0x2a }; + + _http2_frame_header_read (headers, &fh); + HTTP_TEST ((fh.flags == + (HTTP2_FRAME_FLAG_END_HEADERS | HTTP2_FRAME_FLAG_END_STREAM) && + fh.type == HTTP2_FRAME_TYPE_HEADERS && fh.stream_id == 3 && + fh.length == 40), + "frame identified as HEADERS"); + + rv = _http2_frame_read_headers ( + &h, &h_len, headers + HTTP2_FRAME_HEADER_SIZE, fh.length, fh.flags); + HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && h_len == 40 && + *h == headers[HTTP2_FRAME_HEADER_SIZE]), + "HEADERS frame payload parsed") + + vlib_cli_output (vm, "http2_frame_write_headers_header"); + + static void (*_http2_frame_write_headers_header) ( + u32 headers_len, u32 stream_id, u8 flags, u8 * dst); + + _http2_frame_write_headers_header = vlib_get_plugin_symbol ( + "http_plugin.so", "http2_frame_write_headers_header"); + + u8 *p = http2_frame_header_alloc (&buf); + _http2_frame_write_headers_header ( + 40, 3, HTTP2_FRAME_FLAG_END_HEADERS | HTTP2_FRAME_FLAG_END_STREAM, p); + HTTP_TEST ((vec_len (buf) == HTTP2_FRAME_HEADER_SIZE && + !memcmp (buf, headers, HTTP2_FRAME_HEADER_SIZE)), + "HEADERS frame header written"); + vec_free (buf); + + vlib_cli_output (vm, "http2_frame_read_data"); + + static http2_error_t (*_http2_frame_read_data) ( + u8 * *data, u32 * data_len, u8 * payload, u32 payload_len, u8 flags); + + _http2_frame_read_data = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_read_data"); + + u8 *d; + u32 d_len; + u8 data[] = { 0x0, 0x0, 0x9, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, + 0x6e, 0x6f, 0x74, 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64 }; + + _http2_frame_header_read (data, &fh); + HTTP_TEST ((fh.flags == HTTP2_FRAME_FLAG_END_STREAM && + fh.type == HTTP2_FRAME_TYPE_DATA && fh.stream_id == 3 && + fh.length == 9), + "frame identified as DATA"); + + rv = _http2_frame_read_data (&d, &d_len, data + HTTP2_FRAME_HEADER_SIZE, + fh.length, fh.flags); + HTTP_TEST ((rv == HTTP2_ERROR_NO_ERROR && d_len == 9 && + *d == data[HTTP2_FRAME_HEADER_SIZE]), + "DATA frame payload parsed") + + vlib_cli_output (vm, "http2_frame_write_data_header"); + + static void (*_http2_frame_write_data_header) ( + u32 headers_len, u32 stream_id, u8 flags, u8 * dst); + + _http2_frame_write_data_header = + vlib_get_plugin_symbol ("http_plugin.so", "http2_frame_write_data_header"); + + p = http2_frame_header_alloc (&buf); + _http2_frame_write_data_header (9, 3, HTTP2_FRAME_FLAG_END_STREAM, p); + HTTP_TEST ((vec_len (buf) == HTTP2_FRAME_HEADER_SIZE && + !memcmp (buf, data, HTTP2_FRAME_HEADER_SIZE)), + "DATA frame header written"); + vec_free (buf); + + return 0; +} + static clib_error_t * test_http_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) @@ -1039,6 +1319,8 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input, res = http_test_http_header_table (vm); else if (unformat (input, "hpack")) res = http_test_hpack (vm); + else if (unformat (input, "h2-frame")) + res = http_test_h2_frame (vm); else if (unformat (input, "all")) { if ((res = http_test_parse_authority (vm))) @@ -1053,6 +1335,8 @@ test_http_command_fn (vlib_main_t *vm, unformat_input_t *input, goto done; if ((res = http_test_hpack (vm))) goto done; + if ((res = http_test_h2_frame (vm))) + goto done; } else break; |