diff options
author | Lawrence Brakmo <brakmo@fb.com> | 2018-01-25 19:14:08 -0500 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2018-01-25 19:41:14 -0500 |
commit | b73042b8a28e2603ac178295ab96c876ba5a97a1 (patch) | |
tree | c3db00b9c7a9b9816bb3392f9fa59f7d8b4a5e49 | |
parent | 34d367c59233464dbd1f07445c4665099a7128ec (diff) |
bpf: Add write access to tcp_sock and sock fields
This patch adds a macro, SOCK_OPS_SET_FIELD, for writing to
struct tcp_sock or struct sock fields. This required adding a new
field "temp" to struct bpf_sock_ops_kern for temporary storage that
is used by sock_ops_convert_ctx_access. It is used to store and recover
the contents of a register, so the register can be used to store the
address of the sk. Since we cannot overwrite the dst_reg because it
contains the pointer to ctx, nor the src_reg since it contains the value
we want to store, we need an extra register to contain the address
of the sk.
Also adds the macro SOCK_OPS_GET_OR_SET_FIELD that calls one of the
GET or SET macros depending on the value of the TYPE field.
Signed-off-by: Lawrence Brakmo <brakmo@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r-- | include/linux/filter.h | 9 | ||||
-rw-r--r-- | include/net/tcp.h | 2 | ||||
-rw-r--r-- | net/core/filter.c | 48 |
3 files changed, 58 insertions, 1 deletions
diff --git a/include/linux/filter.h b/include/linux/filter.h index 425056c7f96c..daa5a676335f 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h | |||
@@ -1007,6 +1007,15 @@ struct bpf_sock_ops_kern { | |||
1007 | u32 replylong[4]; | 1007 | u32 replylong[4]; |
1008 | }; | 1008 | }; |
1009 | u32 is_fullsock; | 1009 | u32 is_fullsock; |
1010 | u64 temp; /* temp and everything after is not | ||
1011 | * initialized to 0 before calling | ||
1012 | * the BPF program. New fields that | ||
1013 | * should be initialized to 0 should | ||
1014 | * be inserted before temp. | ||
1015 | * temp is scratch storage used by | ||
1016 | * sock_ops_convert_ctx_access | ||
1017 | * as temporary storage of a register. | ||
1018 | */ | ||
1010 | }; | 1019 | }; |
1011 | 1020 | ||
1012 | #endif /* __LINUX_FILTER_H__ */ | 1021 | #endif /* __LINUX_FILTER_H__ */ |
diff --git a/include/net/tcp.h b/include/net/tcp.h index 5a1d26a18599..6092eaff61cf 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
@@ -2011,7 +2011,7 @@ static inline int tcp_call_bpf(struct sock *sk, int op) | |||
2011 | struct bpf_sock_ops_kern sock_ops; | 2011 | struct bpf_sock_ops_kern sock_ops; |
2012 | int ret; | 2012 | int ret; |
2013 | 2013 | ||
2014 | memset(&sock_ops, 0, sizeof(sock_ops)); | 2014 | memset(&sock_ops, 0, offsetof(struct bpf_sock_ops_kern, temp)); |
2015 | if (sk_fullsock(sk)) { | 2015 | if (sk_fullsock(sk)) { |
2016 | sock_ops.is_fullsock = 1; | 2016 | sock_ops.is_fullsock = 1; |
2017 | sock_owned_by_me(sk); | 2017 | sock_owned_by_me(sk); |
diff --git a/net/core/filter.c b/net/core/filter.c index dbb6d2f60680..c356ec02b1a5 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -4491,6 +4491,54 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, | |||
4491 | offsetof(OBJ, OBJ_FIELD)); \ | 4491 | offsetof(OBJ, OBJ_FIELD)); \ |
4492 | } while (0) | 4492 | } while (0) |
4493 | 4493 | ||
4494 | /* Helper macro for adding write access to tcp_sock or sock fields. | ||
4495 | * The macro is called with two registers, dst_reg which contains a pointer | ||
4496 | * to ctx (context) and src_reg which contains the value that should be | ||
4497 | * stored. However, we need an additional register since we cannot overwrite | ||
4498 | * dst_reg because it may be used later in the program. | ||
4499 | * Instead we "borrow" one of the other register. We first save its value | ||
4500 | * into a new (temp) field in bpf_sock_ops_kern, use it, and then restore | ||
4501 | * it at the end of the macro. | ||
4502 | */ | ||
4503 | #define SOCK_OPS_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ) \ | ||
4504 | do { \ | ||
4505 | int reg = BPF_REG_9; \ | ||
4506 | BUILD_BUG_ON(FIELD_SIZEOF(OBJ, OBJ_FIELD) > \ | ||
4507 | FIELD_SIZEOF(struct bpf_sock_ops, BPF_FIELD)); \ | ||
4508 | if (si->dst_reg == reg || si->src_reg == reg) \ | ||
4509 | reg--; \ | ||
4510 | if (si->dst_reg == reg || si->src_reg == reg) \ | ||
4511 | reg--; \ | ||
4512 | *insn++ = BPF_STX_MEM(BPF_DW, si->dst_reg, reg, \ | ||
4513 | offsetof(struct bpf_sock_ops_kern, \ | ||
4514 | temp)); \ | ||
4515 | *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( \ | ||
4516 | struct bpf_sock_ops_kern, \ | ||
4517 | is_fullsock), \ | ||
4518 | reg, si->dst_reg, \ | ||
4519 | offsetof(struct bpf_sock_ops_kern, \ | ||
4520 | is_fullsock)); \ | ||
4521 | *insn++ = BPF_JMP_IMM(BPF_JEQ, reg, 0, 2); \ | ||
4522 | *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF( \ | ||
4523 | struct bpf_sock_ops_kern, sk),\ | ||
4524 | reg, si->dst_reg, \ | ||
4525 | offsetof(struct bpf_sock_ops_kern, sk));\ | ||
4526 | *insn++ = BPF_STX_MEM(BPF_FIELD_SIZEOF(OBJ, OBJ_FIELD), \ | ||
4527 | reg, si->src_reg, \ | ||
4528 | offsetof(OBJ, OBJ_FIELD)); \ | ||
4529 | *insn++ = BPF_LDX_MEM(BPF_DW, reg, si->dst_reg, \ | ||
4530 | offsetof(struct bpf_sock_ops_kern, \ | ||
4531 | temp)); \ | ||
4532 | } while (0) | ||
4533 | |||
4534 | #define SOCK_OPS_GET_OR_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ, TYPE) \ | ||
4535 | do { \ | ||
4536 | if (TYPE == BPF_WRITE) \ | ||
4537 | SOCK_OPS_SET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ); \ | ||
4538 | else \ | ||
4539 | SOCK_OPS_GET_FIELD(BPF_FIELD, OBJ_FIELD, OBJ); \ | ||
4540 | } while (0) | ||
4541 | |||
4494 | case offsetof(struct bpf_sock_ops, snd_cwnd): | 4542 | case offsetof(struct bpf_sock_ops, snd_cwnd): |
4495 | SOCK_OPS_GET_FIELD(snd_cwnd, snd_cwnd, struct tcp_sock); | 4543 | SOCK_OPS_GET_FIELD(snd_cwnd, snd_cwnd, struct tcp_sock); |
4496 | break; | 4544 | break; |