Skip to content
33 changes: 33 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
sudo: required
language: bash
dist: bionic
services:
- docker

env:
global:
- PROJECT_NAME='libbpf'
- AUTHOR_EMAIL="$(git log -1 --pretty=\"%aE\")"
- REPO_ROOT="$TRAVIS_BUILD_DIR"
- CI_ROOT="$REPO_ROOT/travis-ci"
- VMTEST_ROOT="$CI_ROOT/vmtest"

addons:
apt:
packages:
- qemu-kvm
- zstd
- binutils-dev
- elfutils
- libcap-dev
- libelf-dev
- libdw-dev
- python3-docutils

jobs:
include:
- stage: Builds & Tests
name: Kernel LATEST + selftests
language: bash
env: KERNEL=LATEST
script: $CI_ROOT/vmtest/run_vmtest.sh || travis_terminate 1
33 changes: 33 additions & 0 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ enum bpf_return_type {
RET_PTR_TO_SOCK_COMMON_OR_NULL, /* returns a pointer to a sock_common or NULL */
RET_PTR_TO_ALLOC_MEM_OR_NULL, /* returns a pointer to dynamically allocated memory or NULL */
RET_PTR_TO_BTF_ID_OR_NULL, /* returns a pointer to a btf_id or NULL */
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 @@ -385,6 +386,8 @@ enum bpf_reg_type {
PTR_TO_RDONLY_BUF_OR_NULL, /* reg points to a readonly buffer or NULL */
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_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 @@ -1785,6 +1788,7 @@ extern const struct bpf_func_proto bpf_skc_to_tcp_timewait_sock_proto;
extern const struct bpf_func_proto bpf_skc_to_tcp_request_sock_proto;
extern const struct bpf_func_proto bpf_skc_to_udp6_sock_proto;
extern const struct bpf_func_proto bpf_copy_from_user_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 @@ -1841,6 +1845,35 @@ struct sk_reuseport_kern {
u32 reuseport_id;
bool bind_inany;
};

#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 */

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

Expand Down
15 changes: 15 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -3579,6 +3579,15 @@ union bpf_attr {
* the data in *dst*. This is a wrapper of **copy_from_user**\ ().
* Return
* 0 on success, or a negative error in case of failure.
*
* 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 @@ -3730,6 +3739,7 @@ union bpf_attr {
FN(inode_storage_delete), \
FN(d_path), \
FN(copy_from_user), \
FN(mptcp_sock), \
/* */

/* integer value in 'imm' field of BPF_CALL instruction selects which helper
Expand Down Expand Up @@ -4060,6 +4070,11 @@ struct bpf_tcp_sock {
__u32 delivered; /* Total data packets delivered incl. rexmits */
__u32 delivered_ce; /* Like the above but only ECE marked packets */
__u32 icsk_retransmits; /* Number of unrecovered [RTO] timeouts */
__u32 is_mptcp; /* Is MPTCP subflow? */
};

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

struct bpf_sock_tuple {
Expand Down
30 changes: 30 additions & 0 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,13 +392,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 @@ -409,6 +411,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 @@ -427,6 +430,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 @@ -500,6 +505,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 @@ -2177,6 +2184,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 @@ -2786,6 +2795,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 @@ -2944,6 +2956,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 @@ -4997,6 +5012,10 @@ static int check_helper_call(struct bpf_verifier_env *env, int func_id, int insn
mark_reg_known_zero(env, regs, BPF_REG_0);
regs[BPF_REG_0].type = PTR_TO_TCP_SOCK_OR_NULL;
regs[BPF_REG_0].id = ++env->id_gen;
} 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;
regs[BPF_REG_0].id = ++env->id_gen;
} 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 @@ -5328,6 +5347,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 @@ -7048,6 +7069,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 @@ -8411,6 +8434,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 @@ -8938,6 +8963,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 @@ -10076,6 +10103,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
13 changes: 12 additions & 1 deletion net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -5837,7 +5837,7 @@ bool bpf_tcp_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_tcp_sock,
icsk_retransmits))
is_mptcp))
return false;

if (off % size != 0)
Expand Down Expand Up @@ -5971,6 +5971,13 @@ u32 bpf_tcp_sock_convert_ctx_access(enum bpf_access_type type,
case offsetof(struct bpf_tcp_sock, icsk_retransmits):
BPF_INET_SOCK_GET_COMMON(icsk_retransmits);
break;
case offsetof(struct bpf_tcp_sock, is_mptcp):
#ifdef CONFIG_MPTCP
BPF_TCP_SOCK_GET_COMMON(is_mptcp);
#else
*insn++ = BPF_MOV32_IMM(si->dst_reg, 0);
#endif
break;
}

return insn - insn_buf;
Expand Down Expand Up @@ -6883,6 +6890,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_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 <nicolas.rybowski@tessares.net>
*
*/

#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,
};
Loading