#include "vmlinux.h"
#include "beeper.h"
#include "xbpf.h"
#include <bpf/bpf_helpers.h>
#define HEADER_FIELD_MAXLEN BEEPER_H2_FIELD_MAXLEN
#define HEADER_FIELD_MASK (HEADER_FIELD_MAXLEN - 1)
#define STATIC_TABLE_SIZE 61
#define DYNAMIC_TABLE_BASE (STATIC_TABLE_SIZE + 1)
#define SETTINGS_HEADER_TABLE_SIZE 0x1
#define H2_FRAME_HDR_LEN 9
#define H2_HEADERS_FRAME 0x01
#define H2_SETTINGS_FRAME 0x04
#define H2_CONTINUATION_FRAME 0x09
#define H2_END_HEADERS_FLAG 0x04
#define H2_PADDED_FLAG 0x08
#define H2_PRIORITY_FLAG 0x20
#define H2_PRIORITY_LEN 5
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__uint(max_entries, STATIC_TABLE_SIZE+1);
__type(key, u32);
__type(value, struct header_field);
} static_table SEC(".maps");
struct dynamic_table_key {
struct ip4_conn conn;
u32 idx;
};
struct dynamic_table_entry {
struct header_field field;
u32 size;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 16384);
__type(key, struct dynamic_table_key);
__type(value, struct dynamic_table_entry);
} dynamic_table SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, u32);
__type(value, struct dynamic_table_entry);
} dynamic_table_entry SEC(".maps");
struct dynamic_table_info {
u32 count;
u32 size;
u32 max_size;
u32 deleted;
u32 dirty;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 16384);
__type(key, struct ip4_conn);
__type(value, struct dynamic_table_info);
} dynamic_table_info SEC(".maps");
#define S_DEAD 2
#define S_FIELD 3
#define S_KEY_LEN 4
#define S_VAL_LEN 5
#define S_NAME 6
#define S_IDX7_CONT 7
#define S_IDX6_CONT 8
#define S_IDX4_CONT 9
#define S_STG_CONT 10
#define S_KEY_LEN_CONT 11
#define S_KEY_LEN_CONT_HUFF 12
#define S_VAL_LEN_CONT 13
#define S_VAL_LEN_CONT_HUFF 14
#define S_RESERVED 15
#define H2A_NONE 0
#define H2A_INDEXED 1
#define H2A_IDX_NAME 2
#define H2A_LIT_NAME 3
#define H2A_KEY_LEN 4
#define H2A_VAL_LEN 5
#define H2A_TABLE_SIZE 6
#define H2A_INT_START 7
#define H2A_INT_CONT 8
#define H2A_CAPTURE 9
#define H2A_ERR 10
#define H2F_HUFF (1 << 0)
#define H2F_ADD_DT (1 << 1)
#define H2F_CONT (1 << 2)
struct h2_action {
u16 val;
u8 kind;
u8 flags;
};
#define MAX_STATES 1024
#define MAX_TRANS 256
#define MAX_ACTIONS 1024
volatile const struct trans s2ts[MAX_STATES][MAX_TRANS];
volatile const struct h2_action a2as[MAX_ACTIONS];
static __always_inline struct h2_action _action(u16 id) {
return a2as[id & (MAX_ACTIONS - 1)];
}
#define HPACK_HUFF_MAXLEN 30
static const u8 huff_count[HPACK_HUFF_MAXLEN + 2] = {
0, 0, 0, 0, 0, 10, 26, 32, 6, 0, 5, 3, 2, 6, 2, 3,
0, 0, 0, 3, 8, 13, 26, 29, 12, 4, 15, 19, 29, 0, 4, 0
};
static const u32 huff_first_code[HPACK_HUFF_MAXLEN + 2] = {
0, 0, 0, 0, 0, 0, 20, 92, 248, 508, 1016, 2042, 4090, 8184, 16380, 32764,
65534, 131068, 262136, 524272, 1048550, 2097116, 4194258, 8388568, 16777194,
33554412, 67108832, 134217694, 268435426, 536870910, 1073741820, 0
};
#define HPACK_HUFF_STEP(c, b) do { \
code = (code << 1) | (((c) >> (b)) & 1u); \
len++; \
if (len > HPACK_HUFF_MAXLEN) { \
code = 0; \
len = 0; \
} \
else { \
u32 rel = code - huff_first_code[len]; \
if (rel < huff_count[len]) { \
n++; \
code = 0; \
len = 0; \
} \
} \
} while (0)
static __always_inline u32 hpack_huffman_decoded_len(const u8 *src, u16 src__sz) {
u32 code = 0, len = 0, n = 0;
u32 i = 0;
bpf_for (i, 0, src__sz) {
u8 c = src[i];
HPACK_HUFF_STEP(c, 7);
HPACK_HUFF_STEP(c, 6);
HPACK_HUFF_STEP(c, 5);
HPACK_HUFF_STEP(c, 4);
HPACK_HUFF_STEP(c, 3);
HPACK_HUFF_STEP(c, 2);
HPACK_HUFF_STEP(c, 1);
HPACK_HUFF_STEP(c, 0);
}
return n;
}
static __always_inline int _h2_block(const u8 *data, const u8 *data_end, u32 len, u8 type, u8 flags, u16 *start, u16 *end) {
u32 off = H2_FRAME_HDR_LEN;
u32 pad_len = 0;
if (type == H2_HEADERS_FRAME) {
if ((flags & H2_PADDED_FLAG) != 0) {
if (data + off + 1 > data_end) return -1;
pad_len = data[off];
off += 1;
}
if ((flags & H2_PRIORITY_FLAG) != 0) off += H2_PRIORITY_LEN;
}
if (off + pad_len > H2_FRAME_HDR_LEN + len) return -1;
*start = off;
*end = H2_FRAME_HDR_LEN + len - pad_len;
return 0;
}
struct msg_ctx {
u8 *data;
u8 *data_end;
struct ip4_conn conn;
};
static __always_inline struct h2_frame _new_h2_frame(const u8 *data, u8 type, u8 flags) {
return (struct h2_frame) {
.sid = ((u32)data[5] << 24 | (u32)data[6] << 16 | (u32)data[7] << 8 | (u32)data[8]) & 0x7FFFFFFF,
.type = type,
.flags = flags,
};
}
static __always_inline struct msg_ctx _new_msg_ctx(const struct sk_msg_md *msg) {
return (struct msg_ctx) {
.data = msg->data,
.data_end = msg->data_end,
.conn = {
.local = {
.ip4 = msg->local_ip4,
.port = msg->local_port
},
.remote = {
.ip4 = msg->remote_ip4,
.port = bpf_ntohl(msg->remote_port)
}
}
};
}
static __always_inline struct msg_ctx _new_skb_ctx(const struct __sk_buff *skb) {
return (struct msg_ctx) {
.data = (u8 *)(long)skb->data,
.data_end = (u8 *)(long)skb->data_end,
.conn = {
.local = {
.ip4 = skb->local_ip4,
.port = skb->local_port
},
.remote = {
.ip4 = skb->remote_ip4,
.port = bpf_ntohl(skb->remote_port)
}
}
};
}
static __always_inline struct dynamic_table_key _new_dynamic_table_key(const struct ip4_conn *conn, u32 idx) {
return (struct dynamic_table_key) {
.conn = *conn,
.idx = idx
};
}
static __always_inline u32 _get_dynamic_table_index(const struct dynamic_table_info *dt_info __arg_nonnull, u32 idx) {
u32 end_idx = STATIC_TABLE_SIZE + dt_info->count + dt_info->deleted;
return (end_idx - idx) + DYNAMIC_TABLE_BASE;
}
static __always_inline bool _is_valid_hpack_index(const struct dynamic_table_info *dt_info __arg_nonnull, u32 idx) {
return idx > 0 && idx <= STATIC_TABLE_SIZE + dt_info->count;
}
static __always_inline void _extract_match(const struct msg_ctx *ctx, const struct hdr_match *m, bool is_key, u8 **out, u32 *len, bool *huff) {
if (m->in_msg) {
if (ctx->data + m->idx + m->len > ctx->data_end) return;
*out = ctx->data + m->idx;
*len = m->len;
if (huff) *huff = m->huff;
return;
}
struct dynamic_table_entry *entry = NULL;
if (m->idx >= DYNAMIC_TABLE_BASE) {
struct dynamic_table_key key = _new_dynamic_table_key(&ctx->conn, m->idx);
entry = bpf_map_lookup_elem(&dynamic_table, &key);
}
else {
if (m->idx == 0) return;
u32 key = m->idx;
entry = bpf_map_lookup_elem(&static_table, &key);
}
if (entry == NULL) return;
barrier();
if (is_key) {
*out = entry->field.key;
*len = entry->field.key_len;
if (huff) *huff = (entry->field.key_huff != 0);
} else {
*out = entry->field.val;
*len = entry->field.val_len;
if (huff) *huff = (entry->field.val_huff != 0);
}
}
static __always_inline void _next(u16 state, u8 input, u16 *next_state, u16 *action) {
state &= MAX_STATES - 1;
input &= MAX_TRANS - 1;
struct trans t = s2ts[state][input];
if (t.state == 0 && t.action == 0) {
*next_state = S_DEAD;
*action = 0;
return;
}
*next_state = t.state;
*action = t.action;
}
static __always_inline void _get_table_entry(const struct ip4_conn *conn __arg_nonnull, const struct dynamic_table_info *dt_info __arg_nonnull, u32 idx, struct header_field **hf) {
if (!_is_valid_hpack_index(dt_info, idx)) {
*hf = NULL;
return;
}
if (idx > STATIC_TABLE_SIZE) {
if (dt_info->dirty) {
*hf = NULL;
return;
}
u32 dt_idx = _get_dynamic_table_index(dt_info, idx);
struct dynamic_table_key key = _new_dynamic_table_key(conn, dt_idx);
bpf_trace("lookup dt: %d (hpack: %d)", dt_idx, idx);
*hf = (struct header_field *)bpf_map_lookup_elem(&dynamic_table, &key);
}
else {
*hf = bpf_map_lookup_elem(&static_table, &idx);
}
}
static __always_inline int _match_header_key(const u8 *key __arg_nonnull, u16 key__sz) {
u16 s = S_NAME;
u16 j = 0;
int mid = -1;
bpf_for(j, 0, key__sz) {
u16 a = 0;
_next(s, key[j], &s, &a);
struct h2_action act = _action(a);
mid = (act.kind == H2A_CAPTURE) ? (int)(act.val & MAX_MATCH_MASK) : -1;
}
return mid;
}
static __always_inline struct dynamic_table_info* _get_dynamic_table(const struct ip4_conn *conn __arg_nonnull) {
struct dynamic_table_info *info = bpf_map_lookup_elem(&dynamic_table_info, conn);
if (info) return info;
struct dynamic_table_info new_info = {
.count = 0,
.size = 0,
.max_size = 4096,
.deleted = 0,
.dirty = 0,
};
bpf_map_update_elem(&dynamic_table_info, conn, &new_info, BPF_ANY);
return bpf_map_lookup_elem(&dynamic_table_info, conn);
}
static __always_inline u32 _try_evict_dynamic_table_entries(const struct msg_ctx *ctx __arg_nonnull, struct dynamic_table_info *dt_info __arg_nonnull, u32 new_entry_size) {
bpf_trace("dt: try evicting %dB (%d actual entries)", new_entry_size, dt_info->count);
u32 freed = 0;
bpf_repeat(dt_info->count) {
if (dt_info->size + new_entry_size <= dt_info->max_size) break;
u32 idx = DYNAMIC_TABLE_BASE + dt_info->deleted;
struct dynamic_table_key key = _new_dynamic_table_key(&ctx->conn, idx);
struct dynamic_table_entry *entry = bpf_map_lookup_elem(&dynamic_table, &key);
if (!entry) {
bpf_error("dt: no entry at index %d", idx);
break;
}
bpf_trace("dt: evicting %dB entry at index %d", entry->size, idx);
dt_info->size -= entry->size;
dt_info->count--;
dt_info->deleted++;
freed += entry->size;
bpf_map_delete_elem(&dynamic_table, &key);
}
bpf_trace("dt: evicted %dB", freed);
return freed;
}
static __always_inline int _add_dynamic_table_entry(const struct msg_ctx *ctx __arg_nonnull, struct dynamic_table_info *dt_info __arg_nonnull, const struct hdr_match *key __arg_nonnull, const struct hdr_match *val __arg_nonnull) {
if (dt_info->dirty) return -1;
u8 *key_ptr = NULL;
u32 key_len = 0;
bool key_huff = false;
_extract_match(ctx, key, true, &key_ptr, &key_len, &key_huff);
if (!key_ptr) return -1;
u8 *val_ptr = NULL;
u32 val_len = 0;
bool val_huff = false;
_extract_match(ctx, val, false, &val_ptr, &val_len, &val_huff);
if (!val_ptr) return -1;
u32 key_wire_len = key_len;
u32 val_wire_len = val_len;
bool key_cut = (key_len > HEADER_FIELD_MAXLEN);
bool val_cut = (val_len > HEADER_FIELD_MAXLEN);
bpf_clamp_uminmax(key_len, 0, HEADER_FIELD_MAXLEN);
bpf_clamp_uminmax(val_len, 0, HEADER_FIELD_MAXLEN);
int per_cpu_key = 0;
struct dynamic_table_entry *dt_val = bpf_map_lookup_elem(&dynamic_table_entry, &per_cpu_key);
if (!dt_val) return -1;
u32 idx = DYNAMIC_TABLE_BASE + dt_info->count + dt_info->deleted;
struct dynamic_table_key dt_key = _new_dynamic_table_key(&ctx->conn, idx);
__builtin_memset(dt_val, 0, sizeof(*dt_val));
int ret = bpf_probe_read_kernel(dt_val->field.key, key_len, key_ptr);
dt_val->field.key_len = !ret * key_len;
ret = bpf_probe_read_kernel(dt_val->field.val, val_len, val_ptr);
dt_val->field.val_len = !ret * val_len;
dt_val->field.key_huff = key_huff;
dt_val->field.val_huff = val_huff;
if ((key_cut && key_huff) || (val_cut && val_huff)) {
bpf_debug("dt: a Huffman coded field longer than %d bytes, the table has drifted", HEADER_FIELD_MAXLEN);
dt_info->dirty = 1;
return -1;
}
u32 key_len_decoded = key_huff ? hpack_huffman_decoded_len(dt_val->field.key, key_len) : key_wire_len;
u32 val_len_decoded = val_huff ? hpack_huffman_decoded_len(dt_val->field.val, val_len) : val_wire_len;
dt_val->size = key_len_decoded + val_len_decoded + 32;
_try_evict_dynamic_table_entries(ctx, dt_info, dt_val->size);
if (dt_info->size + dt_val->size > dt_info->max_size) {
bpf_debug("dt: entry size %d exceeds max size %d", dt_val->size, dt_info->max_size);
return -1;
}
bpf_map_update_elem(&dynamic_table, &dt_key, dt_val, BPF_ANY);
dt_info->size += dt_val->size;
dt_info->count += 1;
bpf_debug("dt: add with index %d, key size: %d, val size: %d, new total size %d", dt_key.idx, key_len_decoded, val_len_decoded, dt_info->size);
bpf_debug("dt: add key { %d %d %d }", key->idx, key->len, key->in_msg);
bpf_debug("dt: add val { %d %d %d }", val->idx, val->len, val->in_msg);
return 0;
}
static __always_inline int _parse_stg_from(const struct msg_ctx *ctx, u16 start, u16 end, u16 *s, struct parse_res *pres, u16 *null_prefix) {
const u8 *data = ctx->data;
const u8 *data_end = ctx->data_end;
u32 len = (u32)(data_end - data);
bpf_clamp_uminmax(len, 0, MAX_BYTES);
if (end < len) len = end;
if (data + 9 > data_end) return 0;
u8 type = data[3];
u8 flags = data[4];
u32 stream_id = data[5] << 24 | data[6] << 16 | data[7] << 8 | data[8];
struct dynamic_table_info *dt_info = _get_dynamic_table(&ctx->conn);
if (!dt_info) return 0;
u32 i = 0;
u8 j = 0;
u16 id = 0;
u32 val = 0;
bpf_for(i, start, len+1) {
if (data + i + 1 > data_end) break;
u8 c = data[i];
if (null_prefix && c == '\0' && i == *null_prefix) {
*null_prefix = i + 1;
continue;
}
if (j < 2) {
id = (id << 8) | c;
}
else {
val = (val << 8) | c;
}
j++;
if (j == 6) {
if (id == SETTINGS_HEADER_TABLE_SIZE) {
dt_info->max_size = val;
bpf_debug("stg: table header size: %u", val);
}
j = 0;
id = 0;
val = 0;
}
}
return i;
}
struct h2_parse_state {
u16 s;
u32 k;
u32 m;
u32 skip;
bool is_key;
s8 cid;
u8 add_to_dt;
struct hdr_match key;
u32 i;
u32 v;
u8 kind;
u8 flags;
};
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 16384);
__type(key, struct ip4_conn);
__type(value, struct h2_parse_state);
} continued_blocks SEC(".maps");
static __always_inline struct h2_parse_state _new_h2_parse_state(void) {
return (struct h2_parse_state) {
.s = S_FIELD,
.k = 0,
.m = 0,
.skip = 0,
.is_key = false,
.cid = -1,
.add_to_dt = 0,
.key = {
.idx = 0,
.len = 0,
.in_msg = true,
.huff = false,
},
.i = 0,
.v = 0,
.kind = H2A_NONE,
.flags = 0,
};
}
__noinline __weak int _run_action(const struct msg_ctx *ctx __arg_nonnull, struct dynamic_table_info *dt_info __arg_nonnull, struct parse_res *pres __arg_nonnull, struct h2_parse_state *ps __arg_nonnull) {
u32 v = ps->v & MAX_BYTES;
bpf_trace("hdr: %d: kind %d, val %d", ps->i, ps->kind, v);
if (ps->kind == H2A_INDEXED || ps->kind == H2A_IDX_NAME) {
u32 idx = ps->v;
bool in_range = _is_valid_hpack_index(dt_info, idx);
u32 slot = 0;
if (in_range) {
slot = idx > STATIC_TABLE_SIZE ? _get_dynamic_table_index(dt_info, idx) : idx;
if (slot > 0xffff) slot = 0;
}
ps->add_to_dt = (ps->flags & H2F_ADD_DT) != 0;
ps->cid = -1;
ps->key = (struct hdr_match) {
.idx = slot,
.len = 0,
.in_msg = false,
.huff = false,
};
struct header_field *hf = NULL;
_get_table_entry(&ctx->conn, dt_info, idx, &hf);
if (hf == NULL) return 0;
u32 key_len = hf->key_len;
bpf_clamp_uminmax(key_len, 0, HEADER_FIELD_MAXLEN);
int mid = _match_header_key(hf->key, key_len);
if (mid < 0) return 0;
if (ps->kind == H2A_IDX_NAME) {
ps->cid = mid;
return 0;
}
pres->ms[mid & MAX_MATCH_MASK] = (struct hdr_match) {
.idx = slot,
.len = HEADER_FIELD_MASK,
.in_msg = false,
.huff = false,
};
return 0;
}
if (ps->kind == H2A_LIT_NAME) {
ps->add_to_dt = (ps->flags & H2F_ADD_DT) != 0;
ps->cid = -1;
return 0;
}
if (ps->kind == H2A_KEY_LEN) {
ps->key = (struct hdr_match) {
.idx = ps->i + 1,
.len = v,
.in_msg = true,
.huff = (ps->flags & H2F_HUFF) != 0,
};
ps->skip = v;
ps->is_key = true;
if (v == 0) ps->s = S_VAL_LEN;
return 0;
}
if (ps->kind == H2A_VAL_LEN) {
struct hdr_match val = (struct hdr_match) {
.idx = ps->i + 1,
.len = v,
.in_msg = true,
.huff = (ps->flags & H2F_HUFF) != 0,
};
if (ps->add_to_dt) {
_add_dynamic_table_entry(ctx, dt_info, &ps->key, &val);
}
if (ps->cid >= 0) {
pres->ms[ps->cid & MAX_MATCH_MASK] = val;
ps->cid = -1;
}
ps->skip = v;
ps->is_key = false;
return 0;
}
if (ps->kind == H2A_TABLE_SIZE) {
bpf_debug("hdr: table size update: %u", ps->v);
dt_info->max_size = ps->v;
return 0;
}
if (ps->kind == H2A_ERR) {
bpf_debug("hdr: malformed representation at %d", ps->i);
return -1;
}
return 0;
}
static __always_inline int _parse_hdr_from(const struct msg_ctx *ctx, u16 start, u16 end, struct dynamic_table_info *dt_info, struct h2_parse_state *ps, struct parse_res *pres, u16 *null_prefix) {
const u8 *data = ctx->data;
const u8 *data_end = ctx->data_end;
u32 len = (u32)(data_end - data);
bpf_clamp_uminmax(len, 0, MAX_BYTES);
if (end < len) len = end;
u32 i = 0;
bpf_for(i, start, len+1) {
if (i >= len) break;
if (data + i + 1 > data_end) break;
u8 c = data[i];
if (null_prefix && c == '\0' && i == *null_prefix) {
*null_prefix = i + 1;
continue;
}
if (ps->skip > 0) {
if (ps->is_key) {
u16 a = 0;
_next(ps->s, c, &ps->s, &a);
struct h2_action act = _action(a);
ps->cid = (act.kind == H2A_CAPTURE) ? (s8)(act.val & MAX_MATCH_MASK) : -1;
}
ps->skip--;
if (ps->skip == 0 && ps->is_key) ps->s = S_VAL_LEN;
continue;
}
u16 a = 0;
_next(ps->s, c, &ps->s, &a);
struct h2_action act = _action(a);
if (act.kind == H2A_INT_START) {
ps->k = act.val;
ps->m = 0;
continue;
}
if (act.kind == H2A_INT_CONT) {
if (ps->m <= 28) {
ps->k += (u32)(c & 0x7F) << ps->m;
ps->m += 7;
}
continue;
}
ps->i = i;
ps->v = act.val;
if ((act.flags & H2F_CONT) != 0) {
ps->v = ps->k;
if (ps->m <= 28) ps->v += (u32)(c & 0x7F) << ps->m;
}
ps->kind = act.kind;
ps->flags = act.flags;
if (_run_action(ctx, dt_info, pres, ps) < 0) break;
}
return i;
}
static __always_inline int _parse_hdr_frame(const struct msg_ctx *ctx, u16 start, u16 end, u8 type, u8 flags, struct parse_res *pres, u16 *null_prefix) {
struct dynamic_table_info *dt_info = _get_dynamic_table(&ctx->conn);
if (!dt_info) return start;
struct h2_parse_state ps = _new_h2_parse_state();
if (type == H2_CONTINUATION_FRAME) {
struct h2_parse_state *resumed = bpf_map_lookup_elem(&continued_blocks, &ctx->conn);
if (resumed == NULL) {
bpf_debug("hdr: a continuation of a block that was not followed");
return end;
}
ps = *resumed;
}
int res = _parse_hdr_from(ctx, start, end, dt_info, &ps, pres, null_prefix);
if ((flags & H2_END_HEADERS_FLAG) != 0) {
bpf_map_delete_elem(&continued_blocks, &ctx->conn);
return res;
}
if (ps.skip > 0 && !dt_info->dirty) {
bpf_debug("dt: a field split over two frames, the table has drifted");
dt_info->dirty = 1;
}
ps.cid = -1;
ps.add_to_dt = 0;
bpf_map_update_elem(&continued_blocks, &ctx->conn, &ps, BPF_ANY);
return res;
}
SEC("freplace")
int parse_msg(struct sk_msg_md *msg, struct parse_res *pres __arg_nonnull, struct h2_frame *frame __arg_nonnull) {
u8 *data = (u8 *)(long)msg->data;
u8 *data_end = (u8 *)(long)msg->data_end;
if (data + H2_FRAME_HDR_LEN > data_end) return 0;
u32 len = data[0] << 16 | data[1] << 8 | data[2];
u8 type = data[3];
u8 flags = data[4];
u32 frame_len = H2_FRAME_HDR_LEN + len;
*frame = _new_h2_frame(data, type, flags);
bpf_debug("Parsing HTTP/2 message with length %d, type %d, flags %d", len, type, flags);
bool is_hdr = (type == H2_HEADERS_FRAME || type == H2_CONTINUATION_FRAME);
bool is_stg = (type == H2_SETTINGS_FRAME);
if (!is_hdr && !(is_stg && flags == 0)) {
return frame_len;
}
if (bpf_msg_pull_data(msg, 0, frame_len, 0) < 0) {
return -(data_end - data);
}
struct msg_ctx ctx = _new_msg_ctx(msg);
u16 start = 0, end = 0;
if (_h2_block(ctx.data, ctx.data_end, len, type, flags, &start, &end) < 0) return -1;
struct dynamic_table_info *dt_info = _get_dynamic_table(&ctx.conn);
frame->dt_count_before = dt_info ? dt_info->count : 0;
int res;
if (is_hdr) {
res = _parse_hdr_frame(&ctx, start, end, type, flags, pres, NULL);
} else {
u16 s = S_FIELD;
res = _parse_stg_from(&ctx, start, end, &s, pres, NULL);
}
frame->dt_count = dt_info ? dt_info->count : 0;
if (res < end) return -1;
return frame_len;
}
SEC("freplace")
int parse_skb(struct __sk_buff *skb, struct parse_res *pres __arg_nonnull, struct h2_frame *frame __arg_nonnull, u16 *null_prefix) {
u8 *data = (u8 *)(long)skb->data;
u8 *data_end = (u8 *)(long)skb->data_end;
if (data + H2_FRAME_HDR_LEN > data_end) return 0;
u32 len = data[0] << 16 | data[1] << 8 | data[2];
u8 type = data[3];
u8 flags = data[4];
u32 frame_len = H2_FRAME_HDR_LEN + len;
*frame = _new_h2_frame(data, type, flags);
bpf_debug("Parsing HTTP/2 sk_buff with length %d, type %d, flags %d", len, type, flags);
if (type != H2_HEADERS_FRAME && type != H2_CONTINUATION_FRAME) {
return frame_len;
}
if (bpf_skb_pull_data(skb, frame_len) < 0) {
return -(data_end - data);
}
struct msg_ctx ctx = _new_skb_ctx(skb);
u16 start = 0, end = 0;
if (_h2_block(ctx.data, ctx.data_end, len, type, flags, &start, &end) < 0) return -1;
int res = _parse_hdr_frame(&ctx, start, end, type, flags, pres, null_prefix);
if (res < end) return -1;
return frame_len;
}
SEC("freplace")
int parse_buf(const struct bpf_dynptr *buf_ptr, struct ip4_conn *conn, struct parse_res *pres __arg_nonnull, struct h2_frame *frame __arg_nonnull, u16 *null_prefix) {
u8 *data = bpf_dynptr_data(buf_ptr, 0, 9);
if (data == NULL) return -1;
u32 len = data[0] << 16 | data[1] << 8 | data[2];
u8 type = data[3];
u8 flags = data[4];
u32 frame_len = H2_FRAME_HDR_LEN + len;
*frame = _new_h2_frame(data, type, flags);
bpf_debug("Parsing HTTP/2 buf with length %d, type %d, flags %d", len, type, flags);
if (type != H2_HEADERS_FRAME && type != H2_CONTINUATION_FRAME) {
return frame_len;
}
data = bpf_dynptr_data(buf_ptr, 0, frame_len);
if (data == NULL) return -1;
struct msg_ctx ctx = {
.data = data,
.data_end = data + frame_len,
.conn = *conn
};
u16 start = 0, end = 0;
if (_h2_block(ctx.data, ctx.data_end, len, type, flags, &start, &end) < 0) return -1;
return _parse_hdr_frame(&ctx, start, end, type, flags, pres, null_prefix);
}
SEC("freplace")
int get_dt_entry(const struct ip4_conn *conn __arg_nonnull, u32 idx, struct header_field *out __arg_nonnull) {
struct dynamic_table_info *dt_info = bpf_map_lookup_elem(&dynamic_table_info, conn);
if (dt_info == NULL) return -1;
struct header_field *hf = NULL;
_get_table_entry(conn, dt_info, idx, &hf);
if (hf == NULL) return -1;
__builtin_memcpy(out, hf, sizeof(*out));
return 0;
}
SEC("freplace")
bool matched(const struct sk_msg_md *msg, const struct parse_res *pres __arg_nonnull, u8 idx) {
if (idx >= MAX_MATCHES) return false;
struct hdr_match m = pres->ms[idx & MAX_MATCH_MASK];
return (m.len > 0);
}
SEC("freplace")
int extract_match(const struct sk_msg_md *msg, const struct parse_res *pres __arg_nonnull, u8 idx, struct hdr_str *str __arg_nonnull) {
if (idx >= MAX_MATCHES) return -1;
struct hdr_match m = pres->ms[idx & MAX_MATCH_MASK];
if (m.len == 0) return -1;
struct msg_ctx ctx = _new_msg_ctx(msg);
u8 *ptr = NULL;
u32 len = 0;
_extract_match(&ctx, &m, false, &ptr, &len, NULL);
if (ptr == NULL) return -1;
*str = (struct hdr_str) {
.len = len,
.ptr = ptr
};
return 0;
}