aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLawrence Brakmo <brakmo@fb.com>2018-01-25 19:14:08 -0500
committerAlexei Starovoitov <ast@kernel.org>2018-01-25 19:41:14 -0500
commitb73042b8a28e2603ac178295ab96c876ba5a97a1 (patch)
treec3db00b9c7a9b9816bb3392f9fa59f7d8b4a5e49
parent34d367c59233464dbd1f07445c4665099a7128ec (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.h9
-rw-r--r--include/net/tcp.h2
-rw-r--r--net/core/filter.c48
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;