#include "vmlinux.h"
#include "beeper.h"
#include "xbpf.h"
#include <bpf/bpf_helpers.h>
const u16 s_init = 0;
const u16 s_any = 1;
#define H1A_NONE 0
#define H1A_START_CAPTURE 1
#define H1A_END_CAPTURE 2
#define H1F_DONE (1 << 0)
struct h1_action {
u8 kind;
u8 flags;
u8 mid;
};
#define MAX_STATES 512
#define MAX_ACTIONS 256
#define MAX_TRANS 257
#define ANY_TRANS 256
volatile const struct trans s2ts[MAX_STATES][MAX_TRANS];
volatile const struct h1_action a2as[MAX_ACTIONS];
static __always_inline struct h1_action _action(u16 id) {
return a2as[id & (MAX_ACTIONS - 1)];
}
static __always_inline void _next(u16 state, u8 input, u16 *next_state, u16 *action) {
state &= MAX_STATES - 1;
struct trans t = s2ts[state][input];
if (t.state == 0 && t.action == 0) {
t = s2ts[state][ANY_TRANS];
if (t.state == 0 && t.action == 0) {
*next_state = s_any;
*action = 0;
return;
}
}
*next_state = t.state;
*action = t.action;
}
static __always_inline int _parse_from(u8 *data, u8 *data_end, u16 start, struct hdr_match *ms, u32* cidx, u16* s, struct null_prefix *null_prefix) {
u32 len = (u32)(data_end - data);
bpf_clamp_uminmax(len, 0, MAX_BYTES);
if (start >= len) {
return 0;
}
u32 i;
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->len) {
null_prefix->len = i + 1;
continue;
}
u16 a = 0;
_next(*s, c, s, &a);
if (*s == s_any) {
_next(s_any, c, s, &a);
}
struct h1_action act = _action(a);
if (act.kind == H1A_START_CAPTURE) {
u16 mid = act.mid & MAX_MATCH_MASK;
bpf_debug("start capture range (%d) in [%d, ...]", mid, i+1);
cidx[mid] = i + 1;
}
else if (act.kind == H1A_END_CAPTURE) {
u16 mid = act.mid & MAX_MATCH_MASK;
bpf_debug("end capture range (%d) in [%d, %d]", mid, cidx[mid], i - cidx[mid] + 1);
ms[mid] = (struct hdr_match) {
.idx = cidx[mid],
.len = i - cidx[mid] + 1,
.in_msg = true
};
}
if ((act.flags & H1F_DONE) != 0) {
bpf_debug("done parsing at %d", i);
return i+1;
}
}
return -len;
}
SEC("freplace")
int parse_msg(struct sk_msg_md *msg, struct parse_res *pres __arg_nonnull) {
u32 cidx[MAX_MATCHES] = { 0 };
u16 s = s_init;
u8 *data = (u8 *)(long)msg->data;
u8 *data_end = (u8 *)(long)msg->data_end;
int res = _parse_from(data, data_end, 0, pres->ms, cidx, &s, NULL);
if (res < 0 && msg->size > -res) {
if (bpf_msg_pull_data(msg, 0, msg->size, 0) < 0) {
return res;
}
u8 *data = (u8 *)(long)msg->data;
u8 *data_end = (u8 *)(long)msg->data_end;
res = _parse_from(data, data_end, -res, pres->ms, cidx, &s, NULL);
}
return res;
}
SEC("freplace")
int parse_skb(struct __sk_buff *skb, u32 off, struct parse_res *pres __arg_nonnull, struct null_prefix *null_prefix) {
if (off >= MAX_BYTES || off >= skb->len) return 0;
u8 *data = (u8 *)(long)skb->data;
u8 *data_end = (u8 *)(long)skb->data_end;
if (data + skb->len > data_end) {
if (bpf_skb_pull_data(skb, skb->len) < 0) return -1;
data = (u8 *)(long)skb->data;
data_end = (u8 *)(long)skb->data_end;
}
u32 cidx[MAX_MATCHES] = { 0 };
u16 s = s_init;
int res = _parse_from(data, data_end, off, pres->ms, cidx, &s, null_prefix);
return res > 0 ? res - (int)off : res + (int)off;
}
SEC("freplace")
int parse_buf(const struct bpf_dynptr *buf_ptr, u32 len, struct parse_res *pres __arg_nonnull, struct null_prefix *null_prefix) {
u32 cidx[MAX_MATCHES] = { 0 };
u16 s = s_init;
u8 *data = bpf_dynptr_data(buf_ptr, 0, len);
if (data == NULL) return -1;
u8 *data_end = data + len;
int res = _parse_from(data, data_end, 0, pres->ms, cidx, &s, null_prefix);
return res;
}
SEC("freplace")
bool matched(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_msg(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;
u8 *data = (u8 *)(long)msg->data;
u8 *data_end = (u8 *)(long)msg->data_end;
if (data + m.idx + m.len > data_end) return -1;
str->ptr = data + m.idx;
str->len = m.len;
return 0;
}
SEC("freplace")
int extract_match_skb(const struct __sk_buff *skb, 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;
u8 *data = (u8 *)(long)skb->data;
u8 *data_end = (u8 *)(long)skb->data_end;
if (data + m.idx + m.len > data_end) return -1;
str->ptr = data + m.idx;
str->len = m.len;
return 0;
}