bpf: allow programs to write to certain skb fields
allow programs read/write skb->mark, tc_index fields and ((struct qdisc_skb_cb *)cb)->data. mark and tc_index are generically useful in TC. cb[0]-cb[4] are primarily used to pass arguments from one program to another called via bpf_tail_call() which can be seen in sockex3_kern.c example. All fields of 'struct __sk_buff' are readable to socket and tc_cls_act progs. mark, tc_index are writeable from tc_cls_act only. cb[0]-cb[4] are writeable by both sockets and tc_cls_act. Add verifier tests and improve sample code. Signed-off-by: Alexei Starovoitov <ast@plumgrid.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

کامیت شده توسط
David S. Miller

والد
3431205e03
کامیت
d691f9e8d4
@@ -89,7 +89,6 @@ static inline __u32 ipv6_addr_hash(struct __sk_buff *ctx, __u64 off)
|
||||
|
||||
struct globals {
|
||||
struct flow_keys flow;
|
||||
__u32 nhoff;
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") percpu_map = {
|
||||
@@ -139,7 +138,7 @@ static void update_stats(struct __sk_buff *skb, struct globals *g)
|
||||
static __always_inline void parse_ip_proto(struct __sk_buff *skb,
|
||||
struct globals *g, __u32 ip_proto)
|
||||
{
|
||||
__u32 nhoff = g->nhoff;
|
||||
__u32 nhoff = skb->cb[0];
|
||||
int poff;
|
||||
|
||||
switch (ip_proto) {
|
||||
@@ -165,7 +164,7 @@ static __always_inline void parse_ip_proto(struct __sk_buff *skb,
|
||||
if (gre_flags & GRE_SEQ)
|
||||
nhoff += 4;
|
||||
|
||||
g->nhoff = nhoff;
|
||||
skb->cb[0] = nhoff;
|
||||
parse_eth_proto(skb, gre_proto);
|
||||
break;
|
||||
}
|
||||
@@ -195,7 +194,7 @@ PROG(PARSE_IP)(struct __sk_buff *skb)
|
||||
if (!g)
|
||||
return 0;
|
||||
|
||||
nhoff = g->nhoff;
|
||||
nhoff = skb->cb[0];
|
||||
|
||||
if (unlikely(ip_is_fragment(skb, nhoff)))
|
||||
return 0;
|
||||
@@ -210,7 +209,7 @@ PROG(PARSE_IP)(struct __sk_buff *skb)
|
||||
verlen = load_byte(skb, nhoff + 0/*offsetof(struct iphdr, ihl)*/);
|
||||
nhoff += (verlen & 0xF) << 2;
|
||||
|
||||
g->nhoff = nhoff;
|
||||
skb->cb[0] = nhoff;
|
||||
parse_ip_proto(skb, g, ip_proto);
|
||||
return 0;
|
||||
}
|
||||
@@ -223,7 +222,7 @@ PROG(PARSE_IPV6)(struct __sk_buff *skb)
|
||||
if (!g)
|
||||
return 0;
|
||||
|
||||
nhoff = g->nhoff;
|
||||
nhoff = skb->cb[0];
|
||||
|
||||
ip_proto = load_byte(skb,
|
||||
nhoff + offsetof(struct ipv6hdr, nexthdr));
|
||||
@@ -233,25 +232,21 @@ PROG(PARSE_IPV6)(struct __sk_buff *skb)
|
||||
nhoff + offsetof(struct ipv6hdr, daddr));
|
||||
nhoff += sizeof(struct ipv6hdr);
|
||||
|
||||
g->nhoff = nhoff;
|
||||
skb->cb[0] = nhoff;
|
||||
parse_ip_proto(skb, g, ip_proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PROG(PARSE_VLAN)(struct __sk_buff *skb)
|
||||
{
|
||||
struct globals *g = this_cpu_globals();
|
||||
__u32 nhoff, proto;
|
||||
|
||||
if (!g)
|
||||
return 0;
|
||||
|
||||
nhoff = g->nhoff;
|
||||
nhoff = skb->cb[0];
|
||||
|
||||
proto = load_half(skb, nhoff + offsetof(struct vlan_hdr,
|
||||
h_vlan_encapsulated_proto));
|
||||
nhoff += sizeof(struct vlan_hdr);
|
||||
g->nhoff = nhoff;
|
||||
skb->cb[0] = nhoff;
|
||||
|
||||
parse_eth_proto(skb, proto);
|
||||
|
||||
@@ -260,17 +255,13 @@ PROG(PARSE_VLAN)(struct __sk_buff *skb)
|
||||
|
||||
PROG(PARSE_MPLS)(struct __sk_buff *skb)
|
||||
{
|
||||
struct globals *g = this_cpu_globals();
|
||||
__u32 nhoff, label;
|
||||
|
||||
if (!g)
|
||||
return 0;
|
||||
|
||||
nhoff = g->nhoff;
|
||||
nhoff = skb->cb[0];
|
||||
|
||||
label = load_word(skb, nhoff);
|
||||
nhoff += sizeof(struct mpls_label);
|
||||
g->nhoff = nhoff;
|
||||
skb->cb[0] = nhoff;
|
||||
|
||||
if (label & MPLS_LS_S_MASK) {
|
||||
__u8 verlen = load_byte(skb, nhoff);
|
||||
@@ -288,14 +279,10 @@ PROG(PARSE_MPLS)(struct __sk_buff *skb)
|
||||
SEC("socket/0")
|
||||
int main_prog(struct __sk_buff *skb)
|
||||
{
|
||||
struct globals *g = this_cpu_globals();
|
||||
__u32 nhoff = ETH_HLEN;
|
||||
__u32 proto = load_half(skb, 12);
|
||||
|
||||
if (!g)
|
||||
return 0;
|
||||
|
||||
g->nhoff = nhoff;
|
||||
skb->cb[0] = nhoff;
|
||||
parse_eth_proto(skb, proto);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ struct bpf_test {
|
||||
ACCEPT,
|
||||
REJECT
|
||||
} result;
|
||||
enum bpf_prog_type prog_type;
|
||||
};
|
||||
|
||||
static struct bpf_test tests[] = {
|
||||
@@ -743,6 +744,84 @@ static struct bpf_test tests[] = {
|
||||
.errstr = "different pointers",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"check skb->mark is not writeable by sockets",
|
||||
.insns = {
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, mark)),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid bpf_context access",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"check skb->tc_index is not writeable by sockets",
|
||||
.insns = {
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, tc_index)),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid bpf_context access",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"check non-u32 access to cb",
|
||||
.insns = {
|
||||
BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, cb[0])),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid bpf_context access",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"check out of range skb->cb access",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, cb[60])),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.errstr = "invalid bpf_context access",
|
||||
.result = REJECT,
|
||||
.prog_type = BPF_PROG_TYPE_SCHED_ACT,
|
||||
},
|
||||
{
|
||||
"write skb fields from socket prog",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, cb[4])),
|
||||
BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, mark)),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, tc_index)),
|
||||
BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, cb[0])),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, cb[2])),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"write skb fields from tc_cls_act prog",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, cb[0])),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
|
||||
offsetof(struct __sk_buff, mark)),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
|
||||
offsetof(struct __sk_buff, tc_index)),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
|
||||
offsetof(struct __sk_buff, tc_index)),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
|
||||
offsetof(struct __sk_buff, cb[3])),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||
},
|
||||
};
|
||||
|
||||
static int probe_filter_length(struct bpf_insn *fp)
|
||||
@@ -775,6 +854,7 @@ static int test(void)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
struct bpf_insn *prog = tests[i].insns;
|
||||
int prog_type = tests[i].prog_type;
|
||||
int prog_len = probe_filter_length(prog);
|
||||
int *fixup = tests[i].fixup;
|
||||
int map_fd = -1;
|
||||
@@ -789,8 +869,8 @@ static int test(void)
|
||||
}
|
||||
printf("#%d %s ", i, tests[i].descr);
|
||||
|
||||
prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog,
|
||||
prog_len * sizeof(struct bpf_insn),
|
||||
prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
prog, prog_len * sizeof(struct bpf_insn),
|
||||
"GPL", 0);
|
||||
|
||||
if (tests[i].result == ACCEPT) {
|
||||
|
مرجع در شماره جدید
Block a user