|
@@ -2410,6 +2410,12 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static const struct bpf_func_proto *
|
|
|
+xdp_func_proto(enum bpf_func_id func_id)
|
|
|
+{
|
|
|
+ return sk_filter_func_proto(func_id);
|
|
|
+}
|
|
|
+
|
|
|
static bool __is_valid_access(int off, int size, enum bpf_access_type type)
|
|
|
{
|
|
|
if (off < 0 || off >= sizeof(struct __sk_buff))
|
|
@@ -2477,6 +2483,44 @@ static bool tc_cls_act_is_valid_access(int off, int size,
|
|
|
return __is_valid_access(off, size, type);
|
|
|
}
|
|
|
|
|
|
+static bool __is_valid_xdp_access(int off, int size,
|
|
|
+ enum bpf_access_type type)
|
|
|
+{
|
|
|
+ if (off < 0 || off >= sizeof(struct xdp_md))
|
|
|
+ return false;
|
|
|
+ if (off % size != 0)
|
|
|
+ return false;
|
|
|
+ if (size != 4)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+static bool xdp_is_valid_access(int off, int size,
|
|
|
+ enum bpf_access_type type,
|
|
|
+ enum bpf_reg_type *reg_type)
|
|
|
+{
|
|
|
+ if (type == BPF_WRITE)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ switch (off) {
|
|
|
+ case offsetof(struct xdp_md, data):
|
|
|
+ *reg_type = PTR_TO_PACKET;
|
|
|
+ break;
|
|
|
+ case offsetof(struct xdp_md, data_end):
|
|
|
+ *reg_type = PTR_TO_PACKET_END;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return __is_valid_xdp_access(off, size, type);
|
|
|
+}
|
|
|
+
|
|
|
+void bpf_warn_invalid_xdp_action(u32 act)
|
|
|
+{
|
|
|
+ WARN_ONCE(1, "Illegal XDP return value %u, expect packet loss\n", act);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action);
|
|
|
+
|
|
|
static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
|
|
|
int src_reg, int ctx_off,
|
|
|
struct bpf_insn *insn_buf,
|
|
@@ -2628,6 +2672,29 @@ static u32 bpf_net_convert_ctx_access(enum bpf_access_type type, int dst_reg,
|
|
|
return insn - insn_buf;
|
|
|
}
|
|
|
|
|
|
+static u32 xdp_convert_ctx_access(enum bpf_access_type type, int dst_reg,
|
|
|
+ int src_reg, int ctx_off,
|
|
|
+ struct bpf_insn *insn_buf,
|
|
|
+ struct bpf_prog *prog)
|
|
|
+{
|
|
|
+ struct bpf_insn *insn = insn_buf;
|
|
|
+
|
|
|
+ switch (ctx_off) {
|
|
|
+ case offsetof(struct xdp_md, data):
|
|
|
+ *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct xdp_buff, data)),
|
|
|
+ dst_reg, src_reg,
|
|
|
+ offsetof(struct xdp_buff, data));
|
|
|
+ break;
|
|
|
+ case offsetof(struct xdp_md, data_end):
|
|
|
+ *insn++ = BPF_LDX_MEM(bytes_to_bpf_size(FIELD_SIZEOF(struct xdp_buff, data_end)),
|
|
|
+ dst_reg, src_reg,
|
|
|
+ offsetof(struct xdp_buff, data_end));
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return insn - insn_buf;
|
|
|
+}
|
|
|
+
|
|
|
static const struct bpf_verifier_ops sk_filter_ops = {
|
|
|
.get_func_proto = sk_filter_func_proto,
|
|
|
.is_valid_access = sk_filter_is_valid_access,
|
|
@@ -2640,6 +2707,12 @@ static const struct bpf_verifier_ops tc_cls_act_ops = {
|
|
|
.convert_ctx_access = bpf_net_convert_ctx_access,
|
|
|
};
|
|
|
|
|
|
+static const struct bpf_verifier_ops xdp_ops = {
|
|
|
+ .get_func_proto = xdp_func_proto,
|
|
|
+ .is_valid_access = xdp_is_valid_access,
|
|
|
+ .convert_ctx_access = xdp_convert_ctx_access,
|
|
|
+};
|
|
|
+
|
|
|
static struct bpf_prog_type_list sk_filter_type __read_mostly = {
|
|
|
.ops = &sk_filter_ops,
|
|
|
.type = BPF_PROG_TYPE_SOCKET_FILTER,
|
|
@@ -2655,11 +2728,17 @@ static struct bpf_prog_type_list sched_act_type __read_mostly = {
|
|
|
.type = BPF_PROG_TYPE_SCHED_ACT,
|
|
|
};
|
|
|
|
|
|
+static struct bpf_prog_type_list xdp_type __read_mostly = {
|
|
|
+ .ops = &xdp_ops,
|
|
|
+ .type = BPF_PROG_TYPE_XDP,
|
|
|
+};
|
|
|
+
|
|
|
static int __init register_sk_filter_ops(void)
|
|
|
{
|
|
|
bpf_register_prog_type(&sk_filter_type);
|
|
|
bpf_register_prog_type(&sched_cls_type);
|
|
|
bpf_register_prog_type(&sched_act_type);
|
|
|
+ bpf_register_prog_type(&xdp_type);
|
|
|
|
|
|
return 0;
|
|
|
}
|