Skip to content

Commit

Permalink
bpf: add 'bpf_mptcp_sock' structure and helper
Browse files Browse the repository at this point in the history
In order to precisely identify the parent MPTCP connection of a subflow,
it is required to access the mptcp_sock's token which uniquely identify a
MPTCP connection.

This patch adds a new structure 'bpf_mptcp_sock' exposing the 'token' field
of the 'mptcp_sock' extracted from a subflow's 'tcp_sock'. It also adds the
declaration of a new BPF helper of the same name to expose the newly
defined structure in the userspace BPF API.

This is the foundation to expose more MPTCP-specific fields through BPF.

Currently, it is limited to the field 'token' of the msk but it is
easily extensible.

Acked-by: Matthieu Baerts <[email protected]>
Acked-by: Mat Martineau <[email protected]>
Signed-off-by: Nicolas Rybowski <[email protected]>
  • Loading branch information
nrybowski authored and jenkins-tessares committed Dec 9, 2020
1 parent 6dd1da9 commit eed59ab
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 0 deletions.
33 changes: 33 additions & 0 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ enum bpf_return_type {
RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL, /* returns a pointer to a valid memory or a btf_id or NULL */
RET_PTR_TO_MEM_OR_BTF_ID, /* returns a pointer to a valid memory or a btf_id */
RET_PTR_TO_BTF_ID, /* returns a pointer to a btf_id */
RET_PTR_TO_MPTCP_SOCK_OR_NULL, /* returns a pointer to mptcp_sock or NULL */
};

/* eBPF function prototype used by verifier to allow BPF_CALLs from eBPF programs
Expand Down Expand Up @@ -412,6 +413,8 @@ enum bpf_reg_type {
PTR_TO_RDWR_BUF, /* reg points to a read/write buffer */
PTR_TO_RDWR_BUF_OR_NULL, /* reg points to a read/write buffer or NULL */
PTR_TO_PERCPU_BTF_ID, /* reg points to a percpu kernel variable */
PTR_TO_MPTCP_SOCK, /* reg points to struct mptcp_sock */
PTR_TO_MPTCP_SOCK_OR_NULL, /* reg points to struct mptcp_sock or NULL */
};

/* The information passed from prog-specific *_is_valid_access
Expand Down Expand Up @@ -1859,6 +1862,7 @@ extern const struct bpf_func_proto bpf_snprintf_btf_proto;
extern const struct bpf_func_proto bpf_per_cpu_ptr_proto;
extern const struct bpf_func_proto bpf_this_cpu_ptr_proto;
extern const struct bpf_func_proto bpf_ktime_get_coarse_ns_proto;
extern const struct bpf_func_proto bpf_mptcp_sock_proto;

const struct bpf_func_proto *bpf_tracing_func_proto(
enum bpf_func_id func_id, const struct bpf_prog *prog);
Expand Down Expand Up @@ -1915,6 +1919,7 @@ struct sk_reuseport_kern {
u32 reuseport_id;
bool bind_inany;
};

bool bpf_tcp_sock_is_valid_access(int off, int size, enum bpf_access_type type,
struct bpf_insn_access_aux *info);

Expand Down Expand Up @@ -1965,6 +1970,34 @@ static inline u32 bpf_xdp_sock_convert_ctx_access(enum bpf_access_type type,
}
#endif /* CONFIG_INET */

#ifdef CONFIG_MPTCP
bool bpf_mptcp_sock_is_valid_access(int off, int size,
enum bpf_access_type type,
struct bpf_insn_access_aux *info);

u32 bpf_mptcp_sock_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si,
struct bpf_insn *insn_buf,
struct bpf_prog *prog,
u32 *target_size);
#else /* CONFIG_MPTCP */
static inline bool bpf_mptcp_sock_is_valid_access(int off, int size,
enum bpf_access_type type,
struct bpf_insn_access_aux *info)
{
return false;
}

static inline u32 bpf_mptcp_sock_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si,
struct bpf_insn *insn_buf,
struct bpf_prog *prog,
u32 *target_size)
{
return 0;
}
#endif /* CONFIG_MPTCP */

enum bpf_text_poke_type {
BPF_MOD_CALL,
BPF_MOD_JUMP,
Expand Down
13 changes: 13 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -3822,6 +3822,14 @@ union bpf_attr {
* The **hash_algo** is returned on success,
* **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if
* invalid arguments are passed.
*
* struct bpf_mptcp_sock *bpf_mptcp_sock(struct bpf_sock *sk)
* Description
* This helper gets a **struct bpf_mptcp_sock** pointer from a
* **struct bpf_sock** pointer.
* Return
* A **struct bpf_mptcp_sock** pointer on success, or **NULL** in
* case of failure.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
Expand Down Expand Up @@ -3986,6 +3994,7 @@ union bpf_attr {
FN(bprm_opts_set), \
FN(ktime_get_coarse_ns), \
FN(ima_inode_hash), \
FN(mptcp_sock), \
/* */

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
Expand Down Expand Up @@ -4324,6 +4333,10 @@ struct bpf_tcp_sock {
__u32 is_mptcp; /* Is MPTCP subflow? */
};

struct bpf_mptcp_sock {
__u32 token; /* msk token */
};

struct bpf_sock_tuple {
union {
struct {
Expand Down
29 changes: 29 additions & 0 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,13 +395,15 @@ static bool type_is_sk_pointer(enum bpf_reg_type type)
return type == PTR_TO_SOCKET ||
type == PTR_TO_SOCK_COMMON ||
type == PTR_TO_TCP_SOCK ||
type == PTR_TO_MPTCP_SOCK ||
type == PTR_TO_XDP_SOCK;
}

static bool reg_type_not_null(enum bpf_reg_type type)
{
return type == PTR_TO_SOCKET ||
type == PTR_TO_TCP_SOCK ||
type == PTR_TO_MPTCP_SOCK ||
type == PTR_TO_MAP_VALUE ||
type == PTR_TO_SOCK_COMMON;
}
Expand All @@ -412,6 +414,7 @@ static bool reg_type_may_be_null(enum bpf_reg_type type)
type == PTR_TO_SOCKET_OR_NULL ||
type == PTR_TO_SOCK_COMMON_OR_NULL ||
type == PTR_TO_TCP_SOCK_OR_NULL ||
type == PTR_TO_MPTCP_SOCK_OR_NULL ||
type == PTR_TO_BTF_ID_OR_NULL ||
type == PTR_TO_MEM_OR_NULL ||
type == PTR_TO_RDONLY_BUF_OR_NULL ||
Expand All @@ -430,6 +433,8 @@ static bool reg_type_may_be_refcounted_or_null(enum bpf_reg_type type)
type == PTR_TO_SOCKET_OR_NULL ||
type == PTR_TO_TCP_SOCK ||
type == PTR_TO_TCP_SOCK_OR_NULL ||
type == PTR_TO_MPTCP_SOCK ||
type == PTR_TO_MPTCP_SOCK_OR_NULL ||
type == PTR_TO_MEM ||
type == PTR_TO_MEM_OR_NULL;
}
Expand Down Expand Up @@ -517,6 +522,8 @@ static const char * const reg_type_str[] = {
[PTR_TO_SOCK_COMMON_OR_NULL] = "sock_common_or_null",
[PTR_TO_TCP_SOCK] = "tcp_sock",
[PTR_TO_TCP_SOCK_OR_NULL] = "tcp_sock_or_null",
[PTR_TO_MPTCP_SOCK] = "mptcp_sock",
[PTR_TO_MPTCP_SOCK_OR_NULL] = "mptcp_sock_or_null",
[PTR_TO_TP_BUFFER] = "tp_buffer",
[PTR_TO_XDP_SOCK] = "xdp_sock",
[PTR_TO_BTF_ID] = "ptr_",
Expand Down Expand Up @@ -2211,6 +2218,8 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
case PTR_TO_SOCK_COMMON_OR_NULL:
case PTR_TO_TCP_SOCK:
case PTR_TO_TCP_SOCK_OR_NULL:
case PTR_TO_MPTCP_SOCK:
case PTR_TO_MPTCP_SOCK_OR_NULL:
case PTR_TO_XDP_SOCK:
case PTR_TO_BTF_ID:
case PTR_TO_BTF_ID_OR_NULL:
Expand Down Expand Up @@ -2838,6 +2847,9 @@ static int check_sock_access(struct bpf_verifier_env *env, int insn_idx,
case PTR_TO_TCP_SOCK:
valid = bpf_tcp_sock_is_valid_access(off, size, t, &info);
break;
case PTR_TO_MPTCP_SOCK:
valid = bpf_mptcp_sock_is_valid_access(off, size, t, &info);
break;
case PTR_TO_XDP_SOCK:
valid = bpf_xdp_sock_is_valid_access(off, size, t, &info);
break;
Expand Down Expand Up @@ -2996,6 +3008,9 @@ static int check_ptr_alignment(struct bpf_verifier_env *env,
case PTR_TO_TCP_SOCK:
pointer_desc = "tcp_sock ";
break;
case PTR_TO_MPTCP_SOCK:
pointer_desc = "mptcp_sock ";
break;
case PTR_TO_XDP_SOCK:
pointer_desc = "xdp_sock ";
break;
Expand Down Expand Up @@ -5190,6 +5205,9 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
} else if (fn->ret_type == RET_PTR_TO_TCP_SOCK_OR_NULL) {
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_TCP_SOCK_OR_NULL;
} else if (fn->ret_type == RET_PTR_TO_MPTCP_SOCK_OR_NULL) {
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_MPTCP_SOCK_OR_NULL;
} else if (fn->ret_type == RET_PTR_TO_ALLOC_MEM_OR_NULL) {
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_MEM_OR_NULL;
Expand Down Expand Up @@ -5560,6 +5578,8 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
case PTR_TO_SOCK_COMMON_OR_NULL:
case PTR_TO_TCP_SOCK:
case PTR_TO_TCP_SOCK_OR_NULL:
case PTR_TO_MPTCP_SOCK:
case PTR_TO_MPTCP_SOCK_OR_NULL:
case PTR_TO_XDP_SOCK:
verbose(env, "R%d pointer arithmetic on %s prohibited\n",
dst, reg_type_str[ptr_reg->type]);
Expand Down Expand Up @@ -7343,6 +7363,8 @@ static void mark_ptr_or_null_reg(struct bpf_func_state *state,
reg->type = PTR_TO_SOCK_COMMON;
} else if (reg->type == PTR_TO_TCP_SOCK_OR_NULL) {
reg->type = PTR_TO_TCP_SOCK;
} else if (reg->type == PTR_TO_MPTCP_SOCK_OR_NULL) {
reg->type = PTR_TO_MPTCP_SOCK;
} else if (reg->type == PTR_TO_BTF_ID_OR_NULL) {
reg->type = PTR_TO_BTF_ID;
} else if (reg->type == PTR_TO_MEM_OR_NULL) {
Expand Down Expand Up @@ -8825,6 +8847,8 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur,
case PTR_TO_SOCK_COMMON_OR_NULL:
case PTR_TO_TCP_SOCK:
case PTR_TO_TCP_SOCK_OR_NULL:
case PTR_TO_MPTCP_SOCK:
case PTR_TO_MPTCP_SOCK_OR_NULL:
case PTR_TO_XDP_SOCK:
/* Only valid matches are exact, which memcmp() above
* would have accepted
Expand Down Expand Up @@ -9352,6 +9376,8 @@ static bool reg_type_mismatch_ok(enum bpf_reg_type type)
case PTR_TO_SOCK_COMMON_OR_NULL:
case PTR_TO_TCP_SOCK:
case PTR_TO_TCP_SOCK_OR_NULL:
case PTR_TO_MPTCP_SOCK:
case PTR_TO_MPTCP_SOCK_OR_NULL:
case PTR_TO_XDP_SOCK:
case PTR_TO_BTF_ID:
case PTR_TO_BTF_ID_OR_NULL:
Expand Down Expand Up @@ -10614,6 +10640,9 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
case PTR_TO_TCP_SOCK:
convert_ctx_access = bpf_tcp_sock_convert_ctx_access;
break;
case PTR_TO_MPTCP_SOCK:
convert_ctx_access = bpf_mptcp_sock_convert_ctx_access;
break;
case PTR_TO_XDP_SOCK:
convert_ctx_access = bpf_xdp_sock_convert_ctx_access;
break;
Expand Down
4 changes: 4 additions & 0 deletions net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -7294,6 +7294,10 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_tcp_sock:
return &bpf_tcp_sock_proto;
#endif /* CONFIG_INET */
#ifdef CONFIG_MPTCP
case BPF_FUNC_mptcp_sock:
return &bpf_mptcp_sock_proto;
#endif /* CONFIG_MPTCP */
default:
return bpf_sk_base_func_proto(func_id);
}
Expand Down
2 changes: 2 additions & 0 deletions net/mptcp/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ obj-$(CONFIG_INET_MPTCP_DIAG) += mptcp_diag.o
mptcp_crypto_test-objs := crypto_test.o
mptcp_token_test-objs := token_test.o
obj-$(CONFIG_MPTCP_KUNIT_TESTS) += mptcp_crypto_test.o mptcp_token_test.o

obj-$(CONFIG_BPF) += bpf.o
72 changes: 72 additions & 0 deletions net/mptcp/bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0
/* Multipath TCP
*
* Copyright (c) 2020, Tessares SA.
*
* Author: Nicolas Rybowski <[email protected]>
*
*/

#include <linux/bpf.h>

#include "protocol.h"

bool bpf_mptcp_sock_is_valid_access(int off, int size, enum bpf_access_type type,
struct bpf_insn_access_aux *info)
{
if (off < 0 || off >= offsetofend(struct bpf_mptcp_sock, token))
return false;

if (off % size != 0)
return false;

switch (off) {
default:
return size == sizeof(__u32);
}
}

u32 bpf_mptcp_sock_convert_ctx_access(enum bpf_access_type type,
const struct bpf_insn *si,
struct bpf_insn *insn_buf,
struct bpf_prog *prog, u32 *target_size)
{
struct bpf_insn *insn = insn_buf;

#define BPF_MPTCP_SOCK_GET_COMMON(FIELD) \
do { \
BUILD_BUG_ON(sizeof_field(struct mptcp_sock, FIELD) > \
sizeof_field(struct bpf_mptcp_sock, FIELD)); \
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct mptcp_sock, FIELD), \
si->dst_reg, si->src_reg, \
offsetof(struct mptcp_sock, FIELD)); \
} while (0)

if (insn > insn_buf)
return insn - insn_buf;

switch (si->off) {
case offsetof(struct bpf_mptcp_sock, token):
BPF_MPTCP_SOCK_GET_COMMON(token);
break;
}

return insn - insn_buf;
}

BPF_CALL_1(bpf_mptcp_sock, struct sock *, sk)
{
if (sk_fullsock(sk) && sk->sk_protocol == IPPROTO_TCP && sk_is_mptcp(sk)) {
struct mptcp_subflow_context *mptcp_sfc = mptcp_subflow_ctx(sk);

return (unsigned long)mptcp_sfc->conn;
}
return (unsigned long)NULL;
}

const struct bpf_func_proto bpf_mptcp_sock_proto = {
.func = bpf_mptcp_sock,
.gpl_only = false,
.ret_type = RET_PTR_TO_MPTCP_SOCK_OR_NULL,
.arg1_type = ARG_PTR_TO_SOCK_COMMON,
};
2 changes: 2 additions & 0 deletions scripts/bpf_helpers_doc.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ class PrinterHelpers(Printer):
'struct tcp_request_sock',
'struct udp6_sock',
'struct task_struct',
'struct bpf_mptcp_sock',

'struct __sk_buff',
'struct sk_msg_md',
Expand Down Expand Up @@ -482,6 +483,7 @@ class PrinterHelpers(Printer):
'struct path',
'struct btf_ptr',
'struct inode',
'struct bpf_mptcp_sock',
}
mapped_types = {
'u8': '__u8',
Expand Down
13 changes: 13 additions & 0 deletions tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -3822,6 +3822,14 @@ union bpf_attr {
* The **hash_algo** is returned on success,
* **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if
* invalid arguments are passed.
*
* struct bpf_mptcp_sock *bpf_mptcp_sock(struct bpf_sock *sk)
* Description
* This helper gets a **struct bpf_mptcp_sock** pointer from a
* **struct bpf_sock** pointer.
* Return
* A **struct bpf_mptcp_sock** pointer on success, or **NULL** in
* case of failure.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
Expand Down Expand Up @@ -3986,6 +3994,7 @@ union bpf_attr {
FN(bprm_opts_set), \
FN(ktime_get_coarse_ns), \
FN(ima_inode_hash), \
FN(mptcp_sock), \
/* */

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
Expand Down Expand Up @@ -4324,6 +4333,10 @@ struct bpf_tcp_sock {
__u32 is_mptcp; /* Is MPTCP subflow? */
};

struct bpf_mptcp_sock {
__u32 token; /* msk token */
};

struct bpf_sock_tuple {
union {
struct {
Expand Down

0 comments on commit eed59ab

Please sign in to comment.