Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next

Daniel Borkmann says:

====================
pull-request: bpf-next 2018-07-15

The following pull-request contains BPF updates for your *net-next* tree.

The main changes are:

1) Various different arm32 JIT improvements in order to optimize code emission
   and make the JIT code itself more robust, from Russell.

2) Support simultaneous driver and offloaded XDP in order to allow for advanced
   use-cases where some work is offloaded to the NIC and some to the host. Also
   add ability for bpftool to load programs and maps beyond just the cgroup case,
   from Jakub.

3) Add BPF JIT support in nfp for multiplication as well as division. For the
   latter in particular, it uses the reciprocal algorithm to emulate it, from Jiong.

4) Add BTF pretty print functionality to bpftool in plain and JSON output
   format, from Okash.

5) Add build and installation to the BPF helper man page into bpftool, from Quentin.

6) Add a TCP BPF callback for listening sockets which is triggered right after
   the socket transitions to TCP_LISTEN state, from Andrey.

7) Add a new cgroup tree command to bpftool which iterates over the whole cgroup
   tree and prints all attached programs, from Roman.

8) Improve xdp_redirect_cpu sample to support parsing of double VLAN tagged
   packets, from Jesper.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-07-14 18:47:44 -07:00
commit 2aa4a3378a
67 changed files with 3125 additions and 935 deletions

File diff suppressed because it is too large Load Diff

View File

@ -77,11 +77,14 @@
#define ARM_INST_EOR_R 0x00200000
#define ARM_INST_EOR_I 0x02200000
#define ARM_INST_LDRB_I 0x05d00000
#define ARM_INST_LDST__U 0x00800000
#define ARM_INST_LDST__IMM12 0x00000fff
#define ARM_INST_LDRB_I 0x05500000
#define ARM_INST_LDRB_R 0x07d00000
#define ARM_INST_LDRH_I 0x01d000b0
#define ARM_INST_LDRD_I 0x014000d0
#define ARM_INST_LDRH_I 0x015000b0
#define ARM_INST_LDRH_R 0x019000b0
#define ARM_INST_LDR_I 0x05900000
#define ARM_INST_LDR_I 0x05100000
#define ARM_INST_LDR_R 0x07900000
#define ARM_INST_LDM 0x08900000
@ -124,9 +127,10 @@
#define ARM_INST_SBC_R 0x00c00000
#define ARM_INST_SBCS_R 0x00d00000
#define ARM_INST_STR_I 0x05800000
#define ARM_INST_STRB_I 0x05c00000
#define ARM_INST_STRH_I 0x01c000b0
#define ARM_INST_STR_I 0x05000000
#define ARM_INST_STRB_I 0x05400000
#define ARM_INST_STRD_I 0x014000f0
#define ARM_INST_STRH_I 0x014000b0
#define ARM_INST_TST_R 0x01100000
#define ARM_INST_TST_I 0x03100000
@ -183,17 +187,18 @@
#define ARM_EOR_R(rd, rn, rm) _AL3_R(ARM_INST_EOR, rd, rn, rm)
#define ARM_EOR_I(rd, rn, imm) _AL3_I(ARM_INST_EOR, rd, rn, imm)
#define ARM_LDR_I(rt, rn, off) (ARM_INST_LDR_I | (rt) << 12 | (rn) << 16 \
| ((off) & 0xfff))
#define ARM_LDR_R(rt, rn, rm) (ARM_INST_LDR_R | (rt) << 12 | (rn) << 16 \
#define ARM_LDR_R(rt, rn, rm) (ARM_INST_LDR_R | ARM_INST_LDST__U \
| (rt) << 12 | (rn) << 16 \
| (rm))
#define ARM_LDRB_I(rt, rn, off) (ARM_INST_LDRB_I | (rt) << 12 | (rn) << 16 \
| (off))
#define ARM_LDRB_R(rt, rn, rm) (ARM_INST_LDRB_R | (rt) << 12 | (rn) << 16 \
#define ARM_LDR_R_SI(rt, rn, rm, type, imm) \
(ARM_INST_LDR_R | ARM_INST_LDST__U \
| (rt) << 12 | (rn) << 16 \
| (imm) << 7 | (type) << 5 | (rm))
#define ARM_LDRB_R(rt, rn, rm) (ARM_INST_LDRB_R | ARM_INST_LDST__U \
| (rt) << 12 | (rn) << 16 \
| (rm))
#define ARM_LDRH_I(rt, rn, off) (ARM_INST_LDRH_I | (rt) << 12 | (rn) << 16 \
| (((off) & 0xf0) << 4) | ((off) & 0xf))
#define ARM_LDRH_R(rt, rn, rm) (ARM_INST_LDRH_R | (rt) << 12 | (rn) << 16 \
#define ARM_LDRH_R(rt, rn, rm) (ARM_INST_LDRH_R | ARM_INST_LDST__U \
| (rt) << 12 | (rn) << 16 \
| (rm))
#define ARM_LDM(rn, regs) (ARM_INST_LDM | (rn) << 16 | (regs))
@ -254,13 +259,6 @@
#define ARM_SUBS_I(rd, rn, imm) _AL3_I(ARM_INST_SUBS, rd, rn, imm)
#define ARM_SBC_I(rd, rn, imm) _AL3_I(ARM_INST_SBC, rd, rn, imm)
#define ARM_STR_I(rt, rn, off) (ARM_INST_STR_I | (rt) << 12 | (rn) << 16 \
| ((off) & 0xfff))
#define ARM_STRH_I(rt, rn, off) (ARM_INST_STRH_I | (rt) << 12 | (rn) << 16 \
| (((off) & 0xf0) << 4) | ((off) & 0xf))
#define ARM_STRB_I(rt, rn, off) (ARM_INST_STRB_I | (rt) << 12 | (rn) << 16 \
| (((off) & 0xf0) << 4) | ((off) & 0xf))
#define ARM_TST_R(rn, rm) _AL3_R(ARM_INST_TST, 0, rn, rm)
#define ARM_TST_I(rn, imm) _AL3_I(ARM_INST_TST, 0, rn, imm)

View File

@ -219,7 +219,6 @@ int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp)
rc = bnxt_xdp_set(bp, xdp->prog);
break;
case XDP_QUERY_PROG:
xdp->prog_attached = !!bp->xdp_prog;
xdp->prog_id = bp->xdp_prog ? bp->xdp_prog->aux->id : 0;
rc = 0;
break;

View File

@ -1848,7 +1848,6 @@ static int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp)
case XDP_SETUP_PROG:
return nicvf_xdp_setup(nic, xdp->prog);
case XDP_QUERY_PROG:
xdp->prog_attached = !!nic->xdp_prog;
xdp->prog_id = nic->xdp_prog ? nic->xdp_prog->aux->id : 0;
return 0;
default:

View File

@ -11841,7 +11841,6 @@ static int i40e_xdp(struct net_device *dev,
case XDP_SETUP_PROG:
return i40e_xdp_setup(vsi, xdp->prog);
case XDP_QUERY_PROG:
xdp->prog_attached = i40e_enabled_xdp_vsi(vsi);
xdp->prog_id = vsi->xdp_prog ? vsi->xdp_prog->aux->id : 0;
return 0;
default:

View File

@ -9992,7 +9992,6 @@ static int ixgbe_xdp(struct net_device *dev, struct netdev_bpf *xdp)
case XDP_SETUP_PROG:
return ixgbe_xdp_setup(dev, xdp->prog);
case XDP_QUERY_PROG:
xdp->prog_attached = !!(adapter->xdp_prog);
xdp->prog_id = adapter->xdp_prog ?
adapter->xdp_prog->aux->id : 0;
return 0;

View File

@ -4462,7 +4462,6 @@ static int ixgbevf_xdp(struct net_device *dev, struct netdev_bpf *xdp)
case XDP_SETUP_PROG:
return ixgbevf_xdp_setup(dev, xdp->prog);
case XDP_QUERY_PROG:
xdp->prog_attached = !!(adapter->xdp_prog);
xdp->prog_id = adapter->xdp_prog ?
adapter->xdp_prog->aux->id : 0;
return 0;

View File

@ -2926,7 +2926,6 @@ static int mlx4_xdp(struct net_device *dev, struct netdev_bpf *xdp)
return mlx4_xdp_set(dev, xdp->prog);
case XDP_QUERY_PROG:
xdp->prog_id = mlx4_xdp_query(dev);
xdp->prog_attached = !!xdp->prog_id;
return 0;
default:
return -EINVAL;

View File

@ -4196,7 +4196,6 @@ static int mlx5e_xdp(struct net_device *dev, struct netdev_bpf *xdp)
return mlx5e_xdp_set(dev, xdp->prog);
case XDP_QUERY_PROG:
xdp->prog_id = mlx5e_xdp_query(dev);
xdp->prog_attached = !!xdp->prog_id;
return 0;
default:
return -EINVAL;

View File

@ -34,10 +34,11 @@
#define pr_fmt(fmt) "NFP net bpf: " fmt
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/kernel.h>
#include <linux/pkt_cls.h>
#include <linux/reciprocal_div.h>
#include <linux/unistd.h>
#include "main.h"
@ -415,6 +416,60 @@ emit_alu(struct nfp_prog *nfp_prog, swreg dst,
reg.dst_lmextn, reg.src_lmextn);
}
static void
__emit_mul(struct nfp_prog *nfp_prog, enum alu_dst_ab dst_ab, u16 areg,
enum mul_type type, enum mul_step step, u16 breg, bool swap,
bool wr_both, bool dst_lmextn, bool src_lmextn)
{
u64 insn;
insn = OP_MUL_BASE |
FIELD_PREP(OP_MUL_A_SRC, areg) |
FIELD_PREP(OP_MUL_B_SRC, breg) |
FIELD_PREP(OP_MUL_STEP, step) |
FIELD_PREP(OP_MUL_DST_AB, dst_ab) |
FIELD_PREP(OP_MUL_SW, swap) |
FIELD_PREP(OP_MUL_TYPE, type) |
FIELD_PREP(OP_MUL_WR_AB, wr_both) |
FIELD_PREP(OP_MUL_SRC_LMEXTN, src_lmextn) |
FIELD_PREP(OP_MUL_DST_LMEXTN, dst_lmextn);
nfp_prog_push(nfp_prog, insn);
}
static void
emit_mul(struct nfp_prog *nfp_prog, swreg lreg, enum mul_type type,
enum mul_step step, swreg rreg)
{
struct nfp_insn_ur_regs reg;
u16 areg;
int err;
if (type == MUL_TYPE_START && step != MUL_STEP_NONE) {
nfp_prog->error = -EINVAL;
return;
}
if (step == MUL_LAST || step == MUL_LAST_2) {
/* When type is step and step Number is LAST or LAST2, left
* source is used as destination.
*/
err = swreg_to_unrestricted(lreg, reg_none(), rreg, &reg);
areg = reg.dst;
} else {
err = swreg_to_unrestricted(reg_none(), lreg, rreg, &reg);
areg = reg.areg;
}
if (err) {
nfp_prog->error = err;
return;
}
__emit_mul(nfp_prog, reg.dst_ab, areg, type, step, reg.breg, reg.swap,
reg.wr_both, reg.dst_lmextn, reg.src_lmextn);
}
static void
__emit_ld_field(struct nfp_prog *nfp_prog, enum shf_sc sc,
u8 areg, u8 bmask, u8 breg, u8 shift, bool imm8,
@ -1380,6 +1435,133 @@ static void wrp_end32(struct nfp_prog *nfp_prog, swreg reg_in, u8 gpr_out)
SHF_SC_R_ROT, 16);
}
static void
wrp_mul_u32(struct nfp_prog *nfp_prog, swreg dst_hi, swreg dst_lo, swreg lreg,
swreg rreg, bool gen_high_half)
{
emit_mul(nfp_prog, lreg, MUL_TYPE_START, MUL_STEP_NONE, rreg);
emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_1, rreg);
emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_2, rreg);
emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_3, rreg);
emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_32x32, MUL_STEP_4, rreg);
emit_mul(nfp_prog, dst_lo, MUL_TYPE_STEP_32x32, MUL_LAST, reg_none());
if (gen_high_half)
emit_mul(nfp_prog, dst_hi, MUL_TYPE_STEP_32x32, MUL_LAST_2,
reg_none());
else
wrp_immed(nfp_prog, dst_hi, 0);
}
static void
wrp_mul_u16(struct nfp_prog *nfp_prog, swreg dst_hi, swreg dst_lo, swreg lreg,
swreg rreg)
{
emit_mul(nfp_prog, lreg, MUL_TYPE_START, MUL_STEP_NONE, rreg);
emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_16x16, MUL_STEP_1, rreg);
emit_mul(nfp_prog, lreg, MUL_TYPE_STEP_16x16, MUL_STEP_2, rreg);
emit_mul(nfp_prog, dst_lo, MUL_TYPE_STEP_16x16, MUL_LAST, reg_none());
}
static int
wrp_mul(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
bool gen_high_half, bool ropnd_from_reg)
{
swreg multiplier, multiplicand, dst_hi, dst_lo;
const struct bpf_insn *insn = &meta->insn;
u32 lopnd_max, ropnd_max;
u8 dst_reg;
dst_reg = insn->dst_reg;
multiplicand = reg_a(dst_reg * 2);
dst_hi = reg_both(dst_reg * 2 + 1);
dst_lo = reg_both(dst_reg * 2);
lopnd_max = meta->umax_dst;
if (ropnd_from_reg) {
multiplier = reg_b(insn->src_reg * 2);
ropnd_max = meta->umax_src;
} else {
u32 imm = insn->imm;
multiplier = ur_load_imm_any(nfp_prog, imm, imm_b(nfp_prog));
ropnd_max = imm;
}
if (lopnd_max > U16_MAX || ropnd_max > U16_MAX)
wrp_mul_u32(nfp_prog, dst_hi, dst_lo, multiplicand, multiplier,
gen_high_half);
else
wrp_mul_u16(nfp_prog, dst_hi, dst_lo, multiplicand, multiplier);
return 0;
}
static int wrp_div_imm(struct nfp_prog *nfp_prog, u8 dst, u64 imm)
{
swreg dst_both = reg_both(dst), dst_a = reg_a(dst), dst_b = reg_a(dst);
struct reciprocal_value_adv rvalue;
u8 pre_shift, exp;
swreg magic;
if (imm > U32_MAX) {
wrp_immed(nfp_prog, dst_both, 0);
return 0;
}
/* NOTE: because we are using "reciprocal_value_adv" which doesn't
* support "divisor > (1u << 31)", we need to JIT separate NFP sequence
* to handle such case which actually equals to the result of unsigned
* comparison "dst >= imm" which could be calculated using the following
* NFP sequence:
*
* alu[--, dst, -, imm]
* immed[imm, 0]
* alu[dst, imm, +carry, 0]
*
*/
if (imm > 1U << 31) {
swreg tmp_b = ur_load_imm_any(nfp_prog, imm, imm_b(nfp_prog));
emit_alu(nfp_prog, reg_none(), dst_a, ALU_OP_SUB, tmp_b);
wrp_immed(nfp_prog, imm_a(nfp_prog), 0);
emit_alu(nfp_prog, dst_both, imm_a(nfp_prog), ALU_OP_ADD_C,
reg_imm(0));
return 0;
}
rvalue = reciprocal_value_adv(imm, 32);
exp = rvalue.exp;
if (rvalue.is_wide_m && !(imm & 1)) {
pre_shift = fls(imm & -imm) - 1;
rvalue = reciprocal_value_adv(imm >> pre_shift, 32 - pre_shift);
} else {
pre_shift = 0;
}
magic = ur_load_imm_any(nfp_prog, rvalue.m, imm_b(nfp_prog));
if (imm == 1U << exp) {
emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, dst_b,
SHF_SC_R_SHF, exp);
} else if (rvalue.is_wide_m) {
wrp_mul_u32(nfp_prog, imm_both(nfp_prog), reg_none(), dst_a,
magic, true);
emit_alu(nfp_prog, dst_both, dst_a, ALU_OP_SUB,
imm_b(nfp_prog));
emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, dst_b,
SHF_SC_R_SHF, 1);
emit_alu(nfp_prog, dst_both, dst_a, ALU_OP_ADD,
imm_b(nfp_prog));
emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE, dst_b,
SHF_SC_R_SHF, rvalue.sh - 1);
} else {
if (pre_shift)
emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE,
dst_b, SHF_SC_R_SHF, pre_shift);
wrp_mul_u32(nfp_prog, dst_both, reg_none(), dst_a, magic, true);
emit_shf(nfp_prog, dst_both, reg_none(), SHF_OP_NONE,
dst_b, SHF_SC_R_SHF, rvalue.sh);
}
return 0;
}
static int adjust_head(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
swreg tmp = imm_a(nfp_prog), tmp_len = imm_b(nfp_prog);
@ -1684,6 +1866,31 @@ static int sub_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
return 0;
}
static int mul_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
return wrp_mul(nfp_prog, meta, true, true);
}
static int mul_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
return wrp_mul(nfp_prog, meta, true, false);
}
static int div_imm64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
const struct bpf_insn *insn = &meta->insn;
return wrp_div_imm(nfp_prog, insn->dst_reg * 2, insn->imm);
}
static int div_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
/* NOTE: verifier hook has rejected cases for which verifier doesn't
* know whether the source operand is constant or not.
*/
return wrp_div_imm(nfp_prog, meta->insn.dst_reg * 2, meta->umin_src);
}
static int neg_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
const struct bpf_insn *insn = &meta->insn;
@ -1772,8 +1979,8 @@ static int shl_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
u8 dst, src;
dst = insn->dst_reg * 2;
umin = meta->umin;
umax = meta->umax;
umin = meta->umin_src;
umax = meta->umax_src;
if (umin == umax)
return __shl_imm64(nfp_prog, dst, umin);
@ -1881,8 +2088,8 @@ static int shr_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
u8 dst, src;
dst = insn->dst_reg * 2;
umin = meta->umin;
umax = meta->umax;
umin = meta->umin_src;
umax = meta->umax_src;
if (umin == umax)
return __shr_imm64(nfp_prog, dst, umin);
@ -1995,8 +2202,8 @@ static int ashr_reg64(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
u8 dst, src;
dst = insn->dst_reg * 2;
umin = meta->umin;
umax = meta->umax;
umin = meta->umin_src;
umax = meta->umax_src;
if (umin == umax)
return __ashr_imm64(nfp_prog, dst, umin);
@ -2097,6 +2304,26 @@ static int sub_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
return wrp_alu32_imm(nfp_prog, meta, ALU_OP_SUB, !meta->insn.imm);
}
static int mul_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
return wrp_mul(nfp_prog, meta, false, true);
}
static int mul_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
return wrp_mul(nfp_prog, meta, false, false);
}
static int div_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
return div_reg64(nfp_prog, meta);
}
static int div_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
return div_imm64(nfp_prog, meta);
}
static int neg_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
{
u8 dst = meta->insn.dst_reg * 2;
@ -2848,6 +3075,10 @@ static const instr_cb_t instr_cb[256] = {
[BPF_ALU64 | BPF_ADD | BPF_K] = add_imm64,
[BPF_ALU64 | BPF_SUB | BPF_X] = sub_reg64,
[BPF_ALU64 | BPF_SUB | BPF_K] = sub_imm64,
[BPF_ALU64 | BPF_MUL | BPF_X] = mul_reg64,
[BPF_ALU64 | BPF_MUL | BPF_K] = mul_imm64,
[BPF_ALU64 | BPF_DIV | BPF_X] = div_reg64,
[BPF_ALU64 | BPF_DIV | BPF_K] = div_imm64,
[BPF_ALU64 | BPF_NEG] = neg_reg64,
[BPF_ALU64 | BPF_LSH | BPF_X] = shl_reg64,
[BPF_ALU64 | BPF_LSH | BPF_K] = shl_imm64,
@ -2867,6 +3098,10 @@ static const instr_cb_t instr_cb[256] = {
[BPF_ALU | BPF_ADD | BPF_K] = add_imm,
[BPF_ALU | BPF_SUB | BPF_X] = sub_reg,
[BPF_ALU | BPF_SUB | BPF_K] = sub_imm,
[BPF_ALU | BPF_MUL | BPF_X] = mul_reg,
[BPF_ALU | BPF_MUL | BPF_K] = mul_imm,
[BPF_ALU | BPF_DIV | BPF_X] = div_reg,
[BPF_ALU | BPF_DIV | BPF_K] = div_imm,
[BPF_ALU | BPF_NEG] = neg_reg,
[BPF_ALU | BPF_LSH | BPF_K] = shl_imm,
[BPF_ALU | BPF_END | BPF_X] = end_reg32,

View File

@ -66,26 +66,19 @@ nfp_bpf_xdp_offload(struct nfp_app *app, struct nfp_net *nn,
struct bpf_prog *prog, struct netlink_ext_ack *extack)
{
bool running, xdp_running;
int ret;
if (!nfp_net_ebpf_capable(nn))
return -EINVAL;
running = nn->dp.ctrl & NFP_NET_CFG_CTRL_BPF;
xdp_running = running && nn->dp.bpf_offload_xdp;
xdp_running = running && nn->xdp_hw.prog;
if (!prog && !xdp_running)
return 0;
if (prog && running && !xdp_running)
return -EBUSY;
ret = nfp_net_bpf_offload(nn, prog, running, extack);
/* Stop offload if replace not possible */
if (ret)
return ret;
nn->dp.bpf_offload_xdp = !!prog;
return ret;
return nfp_net_bpf_offload(nn, prog, running, extack);
}
static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn)

View File

@ -263,8 +263,10 @@ struct nfp_bpf_reg_state {
* @func_id: function id for call instructions
* @arg1: arg1 for call instructions
* @arg2: arg2 for call instructions
* @umin: copy of core verifier umin_value.
* @umax: copy of core verifier umax_value.
* @umin_src: copy of core verifier umin_value for src opearnd.
* @umax_src: copy of core verifier umax_value for src operand.
* @umin_dst: copy of core verifier umin_value for dst opearnd.
* @umax_dst: copy of core verifier umax_value for dst operand.
* @off: index of first generated machine instruction (in nfp_prog.prog)
* @n: eBPF instruction number
* @flags: eBPF instruction extra optimization flags
@ -300,12 +302,15 @@ struct nfp_insn_meta {
struct bpf_reg_state arg1;
struct nfp_bpf_reg_state arg2;
};
/* We are interested in range info for some operands,
* for example, the shift amount.
/* We are interested in range info for operands of ALU
* operations. For example, shift amount, multiplicand and
* multiplier etc.
*/
struct {
u64 umin;
u64 umax;
u64 umin_src;
u64 umax_src;
u64 umin_dst;
u64 umax_dst;
};
};
unsigned int off;
@ -339,6 +344,11 @@ static inline u8 mbpf_mode(const struct nfp_insn_meta *meta)
return BPF_MODE(meta->insn.code);
}
static inline bool is_mbpf_alu(const struct nfp_insn_meta *meta)
{
return mbpf_class(meta) == BPF_ALU64 || mbpf_class(meta) == BPF_ALU;
}
static inline bool is_mbpf_load(const struct nfp_insn_meta *meta)
{
return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_LDX | BPF_MEM);
@ -384,23 +394,14 @@ static inline bool is_mbpf_xadd(const struct nfp_insn_meta *meta)
return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_XADD);
}
static inline bool is_mbpf_indir_shift(const struct nfp_insn_meta *meta)
static inline bool is_mbpf_mul(const struct nfp_insn_meta *meta)
{
u8 code = meta->insn.code;
bool is_alu, is_shift;
u8 opclass, opcode;
return is_mbpf_alu(meta) && mbpf_op(meta) == BPF_MUL;
}
opclass = BPF_CLASS(code);
is_alu = opclass == BPF_ALU64 || opclass == BPF_ALU;
if (!is_alu)
return false;
opcode = BPF_OP(code);
is_shift = opcode == BPF_LSH || opcode == BPF_RSH || opcode == BPF_ARSH;
if (!is_shift)
return false;
return BPF_SRC(code) == BPF_X;
static inline bool is_mbpf_div(const struct nfp_insn_meta *meta)
{
return is_mbpf_alu(meta) && mbpf_op(meta) == BPF_DIV;
}
/**

View File

@ -190,8 +190,10 @@ nfp_prog_prepare(struct nfp_prog *nfp_prog, const struct bpf_insn *prog,
meta->insn = prog[i];
meta->n = i;
if (is_mbpf_indir_shift(meta))
meta->umin = U64_MAX;
if (is_mbpf_alu(meta)) {
meta->umin_src = U64_MAX;
meta->umin_dst = U64_MAX;
}
list_add_tail(&meta->l, &nfp_prog->insns);
}

View File

@ -516,6 +516,82 @@ nfp_bpf_check_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
return nfp_bpf_check_ptr(nfp_prog, meta, env, meta->insn.dst_reg);
}
static int
nfp_bpf_check_alu(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
struct bpf_verifier_env *env)
{
const struct bpf_reg_state *sreg =
cur_regs(env) + meta->insn.src_reg;
const struct bpf_reg_state *dreg =
cur_regs(env) + meta->insn.dst_reg;
meta->umin_src = min(meta->umin_src, sreg->umin_value);
meta->umax_src = max(meta->umax_src, sreg->umax_value);
meta->umin_dst = min(meta->umin_dst, dreg->umin_value);
meta->umax_dst = max(meta->umax_dst, dreg->umax_value);
/* NFP supports u16 and u32 multiplication.
*
* For ALU64, if either operand is beyond u32's value range, we reject
* it. One thing to note, if the source operand is BPF_K, then we need
* to check "imm" field directly, and we'd reject it if it is negative.
* Because for ALU64, "imm" (with s32 type) is expected to be sign
* extended to s64 which NFP mul doesn't support.
*
* For ALU32, it is fine for "imm" be negative though, because the
* result is 32-bits and there is no difference on the low halve of
* the result for signed/unsigned mul, so we will get correct result.
*/
if (is_mbpf_mul(meta)) {
if (meta->umax_dst > U32_MAX) {
pr_vlog(env, "multiplier is not within u32 value range\n");
return -EINVAL;
}
if (mbpf_src(meta) == BPF_X && meta->umax_src > U32_MAX) {
pr_vlog(env, "multiplicand is not within u32 value range\n");
return -EINVAL;
}
if (mbpf_class(meta) == BPF_ALU64 &&
mbpf_src(meta) == BPF_K && meta->insn.imm < 0) {
pr_vlog(env, "sign extended multiplicand won't be within u32 value range\n");
return -EINVAL;
}
}
/* NFP doesn't have divide instructions, we support divide by constant
* through reciprocal multiplication. Given NFP support multiplication
* no bigger than u32, we'd require divisor and dividend no bigger than
* that as well.
*
* Also eBPF doesn't support signed divide and has enforced this on C
* language level by failing compilation. However LLVM assembler hasn't
* enforced this, so it is possible for negative constant to leak in as
* a BPF_K operand through assembly code, we reject such cases as well.
*/
if (is_mbpf_div(meta)) {
if (meta->umax_dst > U32_MAX) {
pr_vlog(env, "dividend is not within u32 value range\n");
return -EINVAL;
}
if (mbpf_src(meta) == BPF_X) {
if (meta->umin_src != meta->umax_src) {
pr_vlog(env, "divisor is not constant\n");
return -EINVAL;
}
if (meta->umax_src > U32_MAX) {
pr_vlog(env, "divisor is not within u32 value range\n");
return -EINVAL;
}
}
if (mbpf_src(meta) == BPF_K && meta->insn.imm < 0) {
pr_vlog(env, "divide by negative constant is not supported\n");
return -EINVAL;
}
}
return 0;
}
static int
nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
{
@ -551,13 +627,8 @@ nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx)
if (is_mbpf_xadd(meta))
return nfp_bpf_check_xadd(nfp_prog, meta, env);
if (is_mbpf_indir_shift(meta)) {
const struct bpf_reg_state *sreg =
cur_regs(env) + meta->insn.src_reg;
meta->umin = min(meta->umin, sreg->umin_value);
meta->umax = max(meta->umax, sreg->umax_value);
}
if (is_mbpf_alu(meta))
return nfp_bpf_check_alu(nfp_prog, meta, env);
return 0;
}

View File

@ -426,4 +426,32 @@ static inline u32 nfp_get_ind_csr_ctx_ptr_offs(u32 read_offset)
return (read_offset & ~NFP_IND_ME_CTX_PTR_BASE_MASK) | NFP_CSR_CTX_PTR;
}
enum mul_type {
MUL_TYPE_START = 0x00,
MUL_TYPE_STEP_24x8 = 0x01,
MUL_TYPE_STEP_16x16 = 0x02,
MUL_TYPE_STEP_32x32 = 0x03,
};
enum mul_step {
MUL_STEP_1 = 0x00,
MUL_STEP_NONE = MUL_STEP_1,
MUL_STEP_2 = 0x01,
MUL_STEP_3 = 0x02,
MUL_STEP_4 = 0x03,
MUL_LAST = 0x04,
MUL_LAST_2 = 0x05,
};
#define OP_MUL_BASE 0x0f800000000ULL
#define OP_MUL_A_SRC 0x000000003ffULL
#define OP_MUL_B_SRC 0x000000ffc00ULL
#define OP_MUL_STEP 0x00000700000ULL
#define OP_MUL_DST_AB 0x00000800000ULL
#define OP_MUL_SW 0x00040000000ULL
#define OP_MUL_TYPE 0x00180000000ULL
#define OP_MUL_WR_AB 0x20000000000ULL
#define OP_MUL_SRC_LMEXTN 0x40000000000ULL
#define OP_MUL_DST_LMEXTN 0x80000000000ULL
#endif

View File

@ -485,7 +485,6 @@ struct nfp_stat_pair {
* @dev: Backpointer to struct device
* @netdev: Backpointer to net_device structure
* @is_vf: Is the driver attached to a VF?
* @bpf_offload_xdp: Offloaded BPF program is XDP
* @chained_metadata_format: Firemware will use new metadata format
* @rx_dma_dir: Mapping direction for RX buffers
* @rx_dma_off: Offset at which DMA packets (for XDP headroom)
@ -510,7 +509,6 @@ struct nfp_net_dp {
struct net_device *netdev;
u8 is_vf:1;
u8 bpf_offload_xdp:1;
u8 chained_metadata_format:1;
u8 rx_dma_dir;
@ -553,8 +551,8 @@ struct nfp_net_dp {
* @rss_cfg: RSS configuration
* @rss_key: RSS secret key
* @rss_itbl: RSS indirection table
* @xdp_flags: Flags with which XDP prog was loaded
* @xdp_prog: XDP prog (for ctrl path, both DRV and HW modes)
* @xdp: Information about the driver XDP program
* @xdp_hw: Information about the HW XDP program
* @max_r_vecs: Number of allocated interrupt vectors for RX/TX
* @max_tx_rings: Maximum number of TX rings supported by the Firmware
* @max_rx_rings: Maximum number of RX rings supported by the Firmware
@ -610,8 +608,8 @@ struct nfp_net {
u8 rss_key[NFP_NET_CFG_RSS_KEY_SZ];
u8 rss_itbl[NFP_NET_CFG_RSS_ITBL_SZ];
u32 xdp_flags;
struct bpf_prog *xdp_prog;
struct xdp_attachment_info xdp;
struct xdp_attachment_info xdp_hw;
unsigned int max_tx_rings;
unsigned int max_rx_rings;

View File

@ -1710,8 +1710,7 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
}
}
if (xdp_prog && !(rxd->rxd.flags & PCIE_DESC_RX_BPF &&
dp->bpf_offload_xdp) && !meta.portid) {
if (xdp_prog && !meta.portid) {
void *orig_data = rxbuf->frag + pkt_off;
unsigned int dma_off;
int act;
@ -3393,14 +3392,18 @@ static void nfp_net_del_vxlan_port(struct net_device *netdev,
nfp_net_set_vxlan_port(nn, idx, 0);
}
static int
nfp_net_xdp_setup_drv(struct nfp_net *nn, struct bpf_prog *prog,
struct netlink_ext_ack *extack)
static int nfp_net_xdp_setup_drv(struct nfp_net *nn, struct netdev_bpf *bpf)
{
struct bpf_prog *prog = bpf->prog;
struct nfp_net_dp *dp;
int err;
if (!xdp_attachment_flags_ok(&nn->xdp, bpf))
return -EBUSY;
if (!prog == !nn->dp.xdp_prog) {
WRITE_ONCE(nn->dp.xdp_prog, prog);
xdp_attachment_setup(&nn->xdp, bpf);
return 0;
}
@ -3414,38 +3417,26 @@ nfp_net_xdp_setup_drv(struct nfp_net *nn, struct bpf_prog *prog,
dp->rx_dma_off = prog ? XDP_PACKET_HEADROOM - nn->dp.rx_offset : 0;
/* We need RX reconfig to remap the buffers (BIDIR vs FROM_DEV) */
return nfp_net_ring_reconfig(nn, dp, extack);
}
static int
nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog, u32 flags,
struct netlink_ext_ack *extack)
{
struct bpf_prog *drv_prog, *offload_prog;
int err;
if (nn->xdp_prog && (flags ^ nn->xdp_flags) & XDP_FLAGS_MODES)
return -EBUSY;
/* Load both when no flags set to allow easy activation of driver path
* when program is replaced by one which can't be offloaded.
*/
drv_prog = flags & XDP_FLAGS_HW_MODE ? NULL : prog;
offload_prog = flags & XDP_FLAGS_DRV_MODE ? NULL : prog;
err = nfp_net_xdp_setup_drv(nn, drv_prog, extack);
err = nfp_net_ring_reconfig(nn, dp, bpf->extack);
if (err)
return err;
err = nfp_app_xdp_offload(nn->app, nn, offload_prog, extack);
if (err && flags & XDP_FLAGS_HW_MODE)
xdp_attachment_setup(&nn->xdp, bpf);
return 0;
}
static int nfp_net_xdp_setup_hw(struct nfp_net *nn, struct netdev_bpf *bpf)
{
int err;
if (!xdp_attachment_flags_ok(&nn->xdp_hw, bpf))
return -EBUSY;
err = nfp_app_xdp_offload(nn->app, nn, bpf->prog, bpf->extack);
if (err)
return err;
if (nn->xdp_prog)
bpf_prog_put(nn->xdp_prog);
nn->xdp_prog = prog;
nn->xdp_flags = flags;
xdp_attachment_setup(&nn->xdp_hw, bpf);
return 0;
}
@ -3455,16 +3446,13 @@ static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp)
switch (xdp->command) {
case XDP_SETUP_PROG:
return nfp_net_xdp_setup_drv(nn, xdp);
case XDP_SETUP_PROG_HW:
return nfp_net_xdp_setup(nn, xdp->prog, xdp->flags,
xdp->extack);
return nfp_net_xdp_setup_hw(nn, xdp);
case XDP_QUERY_PROG:
xdp->prog_attached = !!nn->xdp_prog;
if (nn->dp.bpf_offload_xdp)
xdp->prog_attached = XDP_ATTACHED_HW;
xdp->prog_id = nn->xdp_prog ? nn->xdp_prog->aux->id : 0;
xdp->prog_flags = nn->xdp_prog ? nn->xdp_flags : 0;
return 0;
return xdp_attachment_query(&nn->xdp, xdp);
case XDP_QUERY_PROG_HW:
return xdp_attachment_query(&nn->xdp_hw, xdp);
default:
return nfp_app_bpf(nn->app, nn, xdp);
}

View File

@ -1116,7 +1116,6 @@ int qede_xdp(struct net_device *dev, struct netdev_bpf *xdp)
case XDP_SETUP_PROG:
return qede_xdp_set(edev, xdp->prog);
case XDP_QUERY_PROG:
xdp->prog_attached = !!edev->xdp_prog;
xdp->prog_id = edev->xdp_prog ? edev->xdp_prog->aux->id : 0;
return 0;
default:

View File

@ -92,7 +92,7 @@ static const struct bpf_prog_offload_ops nsim_bpf_analyzer_ops = {
static bool nsim_xdp_offload_active(struct netdevsim *ns)
{
return ns->xdp_prog_mode == XDP_ATTACHED_HW;
return ns->xdp_hw.prog;
}
static void nsim_prog_set_loaded(struct bpf_prog *prog, bool loaded)
@ -195,14 +195,14 @@ static int nsim_xdp_offload_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
return nsim_bpf_offload(ns, bpf->prog, nsim_xdp_offload_active(ns));
}
static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
static int
nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf,
struct xdp_attachment_info *xdp)
{
int err;
if (ns->xdp_prog && (bpf->flags ^ ns->xdp_flags) & XDP_FLAGS_MODES) {
NSIM_EA(bpf->extack, "program loaded with different flags");
if (!xdp_attachment_flags_ok(xdp, bpf))
return -EBUSY;
}
if (bpf->command == XDP_SETUP_PROG && !ns->bpf_xdpdrv_accept) {
NSIM_EA(bpf->extack, "driver XDP disabled in DebugFS");
@ -219,18 +219,7 @@ static int nsim_xdp_set_prog(struct netdevsim *ns, struct netdev_bpf *bpf)
return err;
}
if (ns->xdp_prog)
bpf_prog_put(ns->xdp_prog);
ns->xdp_prog = bpf->prog;
ns->xdp_flags = bpf->flags;
if (!bpf->prog)
ns->xdp_prog_mode = XDP_ATTACHED_NONE;
else if (bpf->command == XDP_SETUP_PROG)
ns->xdp_prog_mode = XDP_ATTACHED_DRV;
else
ns->xdp_prog_mode = XDP_ATTACHED_HW;
xdp_attachment_setup(xdp, bpf);
return 0;
}
@ -290,10 +279,6 @@ static int nsim_setup_prog_checks(struct netdevsim *ns, struct netdev_bpf *bpf)
NSIM_EA(bpf->extack, "MTU too large w/ XDP enabled");
return -EINVAL;
}
if (nsim_xdp_offload_active(ns)) {
NSIM_EA(bpf->extack, "xdp offload active, can't load drv prog");
return -EBUSY;
}
return 0;
}
@ -567,22 +552,21 @@ int nsim_bpf(struct net_device *dev, struct netdev_bpf *bpf)
nsim_bpf_destroy_prog(bpf->offload.prog);
return 0;
case XDP_QUERY_PROG:
bpf->prog_attached = ns->xdp_prog_mode;
bpf->prog_id = ns->xdp_prog ? ns->xdp_prog->aux->id : 0;
bpf->prog_flags = ns->xdp_prog ? ns->xdp_flags : 0;
return 0;
return xdp_attachment_query(&ns->xdp, bpf);
case XDP_QUERY_PROG_HW:
return xdp_attachment_query(&ns->xdp_hw, bpf);
case XDP_SETUP_PROG:
err = nsim_setup_prog_checks(ns, bpf);
if (err)
return err;
return nsim_xdp_set_prog(ns, bpf);
return nsim_xdp_set_prog(ns, bpf, &ns->xdp);
case XDP_SETUP_PROG_HW:
err = nsim_setup_prog_hw_checks(ns, bpf);
if (err)
return err;
return nsim_xdp_set_prog(ns, bpf);
return nsim_xdp_set_prog(ns, bpf, &ns->xdp_hw);
case BPF_OFFLOAD_MAP_ALLOC:
if (!ns->bpf_map_accept)
return -EOPNOTSUPP;
@ -637,6 +621,7 @@ void nsim_bpf_uninit(struct netdevsim *ns)
{
WARN_ON(!list_empty(&ns->bpf_bound_progs));
WARN_ON(!list_empty(&ns->bpf_bound_maps));
WARN_ON(ns->xdp_prog);
WARN_ON(ns->xdp.prog);
WARN_ON(ns->xdp_hw.prog);
WARN_ON(ns->bpf_offloaded);
}

View File

@ -228,8 +228,7 @@ static int nsim_change_mtu(struct net_device *dev, int new_mtu)
{
struct netdevsim *ns = netdev_priv(dev);
if (ns->xdp_prog_mode == XDP_ATTACHED_DRV &&
new_mtu > NSIM_XDP_MAX_MTU)
if (ns->xdp.prog && new_mtu > NSIM_XDP_MAX_MTU)
return -EBUSY;
dev->mtu = new_mtu;

View File

@ -18,6 +18,7 @@
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/u64_stats_sync.h>
#include <net/xdp.h>
#define DRV_NAME "netdevsim"
@ -67,9 +68,8 @@ struct netdevsim {
struct bpf_prog *bpf_offloaded;
u32 bpf_offloaded_id;
u32 xdp_flags;
int xdp_prog_mode;
struct bpf_prog *xdp_prog;
struct xdp_attachment_info xdp;
struct xdp_attachment_info xdp_hw;
u32 prog_id_gen;

View File

@ -1269,7 +1269,6 @@ static int tun_xdp(struct net_device *dev, struct netdev_bpf *xdp)
return tun_xdp_set(dev, xdp->prog, xdp->extack);
case XDP_QUERY_PROG:
xdp->prog_id = tun_xdp_query(dev);
xdp->prog_attached = !!xdp->prog_id;
return 0;
default:
return -EINVAL;

View File

@ -2343,7 +2343,6 @@ static int virtnet_xdp(struct net_device *dev, struct netdev_bpf *xdp)
return virtnet_xdp_set(dev, xdp->prog, xdp->extack);
case XDP_QUERY_PROG:
xdp->prog_id = virtnet_xdp_query(dev);
xdp->prog_attached = !!xdp->prog_id;
return 0;
default:
return -EINVAL;

View File

@ -823,11 +823,8 @@ enum bpf_netdev_command {
*/
XDP_SETUP_PROG,
XDP_SETUP_PROG_HW,
/* Check if a bpf program is set on the device. The callee should
* set @prog_attached to one of XDP_ATTACHED_* values, note that "true"
* is equivalent to XDP_ATTACHED_DRV.
*/
XDP_QUERY_PROG,
XDP_QUERY_PROG_HW,
/* BPF program for offload callbacks, invoked at program load time. */
BPF_OFFLOAD_VERIFIER_PREP,
BPF_OFFLOAD_TRANSLATE,
@ -851,9 +848,8 @@ struct netdev_bpf {
struct bpf_prog *prog;
struct netlink_ext_ack *extack;
};
/* XDP_QUERY_PROG */
/* XDP_QUERY_PROG, XDP_QUERY_PROG_HW */
struct {
u8 prog_attached;
u32 prog_id;
/* flags with which program was installed */
u32 prog_flags;
@ -3560,8 +3556,8 @@ struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf);
int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
int fd, u32 flags);
void __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op,
struct netdev_bpf *xdp);
u32 __dev_xdp_query(struct net_device *dev, bpf_op_t xdp_op,
enum bpf_netdev_command cmd);
int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);

View File

@ -25,6 +25,9 @@ struct reciprocal_value {
u8 sh1, sh2;
};
/* "reciprocal_value" and "reciprocal_divide" together implement the basic
* version of the algorithm described in Figure 4.1 of the paper.
*/
struct reciprocal_value reciprocal_value(u32 d);
static inline u32 reciprocal_divide(u32 a, struct reciprocal_value R)
@ -33,4 +36,69 @@ static inline u32 reciprocal_divide(u32 a, struct reciprocal_value R)
return (t + ((a - t) >> R.sh1)) >> R.sh2;
}
struct reciprocal_value_adv {
u32 m;
u8 sh, exp;
bool is_wide_m;
};
/* "reciprocal_value_adv" implements the advanced version of the algorithm
* described in Figure 4.2 of the paper except when "divisor > (1U << 31)" whose
* ceil(log2(d)) result will be 32 which then requires u128 divide on host. The
* exception case could be easily handled before calling "reciprocal_value_adv".
*
* The advanced version requires more complex calculation to get the reciprocal
* multiplier and other control variables, but then could reduce the required
* emulation operations.
*
* It makes no sense to use this advanced version for host divide emulation,
* those extra complexities for calculating multiplier etc could completely
* waive our saving on emulation operations.
*
* However, it makes sense to use it for JIT divide code generation for which
* we are willing to trade performance of JITed code with that of host. As shown
* by the following pseudo code, the required emulation operations could go down
* from 6 (the basic version) to 3 or 4.
*
* To use the result of "reciprocal_value_adv", suppose we want to calculate
* n/d, the pseudo C code will be:
*
* struct reciprocal_value_adv rvalue;
* u8 pre_shift, exp;
*
* // handle exception case.
* if (d >= (1U << 31)) {
* result = n >= d;
* return;
* }
*
* rvalue = reciprocal_value_adv(d, 32)
* exp = rvalue.exp;
* if (rvalue.is_wide_m && !(d & 1)) {
* // floor(log2(d & (2^32 -d)))
* pre_shift = fls(d & -d) - 1;
* rvalue = reciprocal_value_adv(d >> pre_shift, 32 - pre_shift);
* } else {
* pre_shift = 0;
* }
*
* // code generation starts.
* if (imm == 1U << exp) {
* result = n >> exp;
* } else if (rvalue.is_wide_m) {
* // pre_shift must be zero when reached here.
* t = (n * rvalue.m) >> 32;
* result = n - t;
* result >>= 1;
* result += t;
* result >>= rvalue.sh - 1;
* } else {
* if (pre_shift)
* result = n >> pre_shift;
* result = ((u64)result * rvalue.m) >> 32;
* result >>= rvalue.sh;
* }
*/
struct reciprocal_value_adv reciprocal_value_adv(u32 d, u8 prec);
#endif /* _LINUX_RECIPROCAL_DIV_H */

View File

@ -144,4 +144,17 @@ xdp_data_meta_unsupported(const struct xdp_buff *xdp)
return unlikely(xdp->data_meta > xdp->data);
}
struct xdp_attachment_info {
struct bpf_prog *prog;
u32 flags;
};
struct netdev_bpf;
int xdp_attachment_query(struct xdp_attachment_info *info,
struct netdev_bpf *bpf);
bool xdp_attachment_flags_ok(struct xdp_attachment_info *info,
struct netdev_bpf *bpf);
void xdp_attachment_setup(struct xdp_attachment_info *info,
struct netdev_bpf *bpf);
#endif /* __LINUX_NET_XDP_H__ */

View File

@ -1826,7 +1826,7 @@ union bpf_attr {
* A non-negative value equal to or less than *size* on success,
* or a negative error in case of failure.
*
* int skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header)
* int bpf_skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header)
* Description
* This helper is similar to **bpf_skb_load_bytes**\ () in that
* it provides an easy way to load *len* bytes from *offset*
@ -1877,7 +1877,7 @@ union bpf_attr {
* * < 0 if any input argument is invalid
* * 0 on success (packet is forwarded, nexthop neighbor exists)
* * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
* * packet is not forwarded or needs assist from full stack
* packet is not forwarded or needs assist from full stack
*
* int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags)
* Description
@ -2033,7 +2033,6 @@ union bpf_attr {
* This helper is only available is the kernel was compiled with
* the **CONFIG_BPF_LIRC_MODE2** configuration option set to
* "**y**".
*
* Return
* 0
*
@ -2053,7 +2052,6 @@ union bpf_attr {
* This helper is only available is the kernel was compiled with
* the **CONFIG_BPF_LIRC_MODE2** configuration option set to
* "**y**".
*
* Return
* 0
*
@ -2557,6 +2555,9 @@ enum {
* Arg1: old_state
* Arg2: new_state
*/
BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after
* socket transition to LISTEN state.
*/
};
/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect

View File

@ -920,6 +920,7 @@ enum {
XDP_ATTACHED_DRV,
XDP_ATTACHED_SKB,
XDP_ATTACHED_HW,
XDP_ATTACHED_MULTI,
};
enum {
@ -928,6 +929,9 @@ enum {
IFLA_XDP_ATTACHED,
IFLA_XDP_FLAGS,
IFLA_XDP_PROG_ID,
IFLA_XDP_DRV_PROG_ID,
IFLA_XDP_SKB_PROG_ID,
IFLA_XDP_HW_PROG_ID,
__IFLA_XDP_MAX,
};

View File

@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/bug.h>
#include <linux/kernel.h>
#include <asm/div64.h>
#include <linux/reciprocal_div.h>
@ -26,3 +27,43 @@ struct reciprocal_value reciprocal_value(u32 d)
return R;
}
EXPORT_SYMBOL(reciprocal_value);
struct reciprocal_value_adv reciprocal_value_adv(u32 d, u8 prec)
{
struct reciprocal_value_adv R;
u32 l, post_shift;
u64 mhigh, mlow;
/* ceil(log2(d)) */
l = fls(d - 1);
/* NOTE: mlow/mhigh could overflow u64 when l == 32. This case needs to
* be handled before calling "reciprocal_value_adv", please see the
* comment at include/linux/reciprocal_div.h.
*/
WARN(l == 32,
"ceil(log2(0x%08x)) == 32, %s doesn't support such divisor",
d, __func__);
post_shift = l;
mlow = 1ULL << (32 + l);
do_div(mlow, d);
mhigh = (1ULL << (32 + l)) + (1ULL << (32 + l - prec));
do_div(mhigh, d);
for (; post_shift > 0; post_shift--) {
u64 lo = mlow >> 1, hi = mhigh >> 1;
if (lo >= hi)
break;
mlow = lo;
mhigh = hi;
}
R.m = (u32)mhigh;
R.sh = post_shift;
R.exp = l;
R.is_wide_m = mhigh > U32_MAX;
return R;
}
EXPORT_SYMBOL(reciprocal_value_adv);

View File

@ -5042,7 +5042,6 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp)
break;
case XDP_QUERY_PROG:
xdp->prog_attached = !!old;
xdp->prog_id = old ? old->aux->id : 0;
break;
@ -7706,23 +7705,21 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down)
}
EXPORT_SYMBOL(dev_change_proto_down);
void __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op,
struct netdev_bpf *xdp)
{
memset(xdp, 0, sizeof(*xdp));
xdp->command = XDP_QUERY_PROG;
/* Query must always succeed. */
WARN_ON(bpf_op(dev, xdp) < 0);
}
static u8 __dev_xdp_attached(struct net_device *dev, bpf_op_t bpf_op)
u32 __dev_xdp_query(struct net_device *dev, bpf_op_t bpf_op,
enum bpf_netdev_command cmd)
{
struct netdev_bpf xdp;
__dev_xdp_query(dev, bpf_op, &xdp);
if (!bpf_op)
return 0;
return xdp.prog_attached;
memset(&xdp, 0, sizeof(xdp));
xdp.command = cmd;
/* Query must always succeed. */
WARN_ON(bpf_op(dev, &xdp) < 0 && cmd == XDP_QUERY_PROG);
return xdp.prog_id;
}
static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op,
@ -7756,12 +7753,19 @@ static void dev_xdp_uninstall(struct net_device *dev)
if (!ndo_bpf)
return;
__dev_xdp_query(dev, ndo_bpf, &xdp);
if (xdp.prog_attached == XDP_ATTACHED_NONE)
return;
memset(&xdp, 0, sizeof(xdp));
xdp.command = XDP_QUERY_PROG;
WARN_ON(ndo_bpf(dev, &xdp));
if (xdp.prog_id)
WARN_ON(dev_xdp_install(dev, ndo_bpf, NULL, xdp.prog_flags,
NULL));
/* Program removal should always succeed */
WARN_ON(dev_xdp_install(dev, ndo_bpf, NULL, xdp.prog_flags, NULL));
/* Remove HW offload */
memset(&xdp, 0, sizeof(xdp));
xdp.command = XDP_QUERY_PROG_HW;
if (!ndo_bpf(dev, &xdp) && xdp.prog_id)
WARN_ON(dev_xdp_install(dev, ndo_bpf, NULL, xdp.prog_flags,
NULL));
}
/**
@ -7777,12 +7781,15 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
int fd, u32 flags)
{
const struct net_device_ops *ops = dev->netdev_ops;
enum bpf_netdev_command query;
struct bpf_prog *prog = NULL;
bpf_op_t bpf_op, bpf_chk;
int err;
ASSERT_RTNL();
query = flags & XDP_FLAGS_HW_MODE ? XDP_QUERY_PROG_HW : XDP_QUERY_PROG;
bpf_op = bpf_chk = ops->ndo_bpf;
if (!bpf_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE)))
return -EOPNOTSUPP;
@ -7792,10 +7799,11 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
bpf_chk = generic_xdp_install;
if (fd >= 0) {
if (bpf_chk && __dev_xdp_attached(dev, bpf_chk))
if (__dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG) ||
__dev_xdp_query(dev, bpf_chk, XDP_QUERY_PROG_HW))
return -EEXIST;
if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) &&
__dev_xdp_attached(dev, bpf_op))
__dev_xdp_query(dev, bpf_op, query))
return -EBUSY;
prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP,

View File

@ -964,7 +964,8 @@ static size_t rtnl_xdp_size(void)
{
size_t xdp_size = nla_total_size(0) + /* nest IFLA_XDP */
nla_total_size(1) + /* XDP_ATTACHED */
nla_total_size(4); /* XDP_PROG_ID */
nla_total_size(4) + /* XDP_PROG_ID (or 1st mode) */
nla_total_size(4); /* XDP_<mode>_PROG_ID */
return xdp_size;
}
@ -1353,27 +1354,51 @@ static int rtnl_fill_link_ifmap(struct sk_buff *skb, struct net_device *dev)
return 0;
}
static u8 rtnl_xdp_attached_mode(struct net_device *dev, u32 *prog_id)
static u32 rtnl_xdp_prog_skb(struct net_device *dev)
{
const struct net_device_ops *ops = dev->netdev_ops;
const struct bpf_prog *generic_xdp_prog;
struct netdev_bpf xdp;
ASSERT_RTNL();
*prog_id = 0;
generic_xdp_prog = rtnl_dereference(dev->xdp_prog);
if (generic_xdp_prog) {
*prog_id = generic_xdp_prog->aux->id;
return XDP_ATTACHED_SKB;
}
if (!ops->ndo_bpf)
return XDP_ATTACHED_NONE;
if (!generic_xdp_prog)
return 0;
return generic_xdp_prog->aux->id;
}
__dev_xdp_query(dev, ops->ndo_bpf, &xdp);
*prog_id = xdp.prog_id;
static u32 rtnl_xdp_prog_drv(struct net_device *dev)
{
return __dev_xdp_query(dev, dev->netdev_ops->ndo_bpf, XDP_QUERY_PROG);
}
return xdp.prog_attached;
static u32 rtnl_xdp_prog_hw(struct net_device *dev)
{
return __dev_xdp_query(dev, dev->netdev_ops->ndo_bpf,
XDP_QUERY_PROG_HW);
}
static int rtnl_xdp_report_one(struct sk_buff *skb, struct net_device *dev,
u32 *prog_id, u8 *mode, u8 tgt_mode, u32 attr,
u32 (*get_prog_id)(struct net_device *dev))
{
u32 curr_id;
int err;
curr_id = get_prog_id(dev);
if (!curr_id)
return 0;
*prog_id = curr_id;
err = nla_put_u32(skb, attr, curr_id);
if (err)
return err;
if (*mode != XDP_ATTACHED_NONE)
*mode = XDP_ATTACHED_MULTI;
else
*mode = tgt_mode;
return 0;
}
static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev)
@ -1381,17 +1406,29 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev)
struct nlattr *xdp;
u32 prog_id;
int err;
u8 mode;
xdp = nla_nest_start(skb, IFLA_XDP);
if (!xdp)
return -EMSGSIZE;
err = nla_put_u8(skb, IFLA_XDP_ATTACHED,
rtnl_xdp_attached_mode(dev, &prog_id));
prog_id = 0;
mode = XDP_ATTACHED_NONE;
if (rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_SKB,
IFLA_XDP_SKB_PROG_ID, rtnl_xdp_prog_skb))
goto err_cancel;
if (rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_DRV,
IFLA_XDP_DRV_PROG_ID, rtnl_xdp_prog_drv))
goto err_cancel;
if (rtnl_xdp_report_one(skb, dev, &prog_id, &mode, XDP_ATTACHED_HW,
IFLA_XDP_HW_PROG_ID, rtnl_xdp_prog_hw))
goto err_cancel;
err = nla_put_u8(skb, IFLA_XDP_ATTACHED, mode);
if (err)
goto err_cancel;
if (prog_id) {
if (prog_id && mode != XDP_ATTACHED_MULTI) {
err = nla_put_u32(skb, IFLA_XDP_PROG_ID, prog_id);
if (err)
goto err_cancel;

View File

@ -3,8 +3,11 @@
* Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc.
* Released under terms in GPL version 2. See COPYING.
*/
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/idr.h>
#include <linux/rhashtable.h>
@ -370,3 +373,34 @@ void xdp_return_buff(struct xdp_buff *xdp)
__xdp_return(xdp->data, &xdp->rxq->mem, true, xdp->handle);
}
EXPORT_SYMBOL_GPL(xdp_return_buff);
int xdp_attachment_query(struct xdp_attachment_info *info,
struct netdev_bpf *bpf)
{
bpf->prog_id = info->prog ? info->prog->aux->id : 0;
bpf->prog_flags = info->prog ? info->flags : 0;
return 0;
}
EXPORT_SYMBOL_GPL(xdp_attachment_query);
bool xdp_attachment_flags_ok(struct xdp_attachment_info *info,
struct netdev_bpf *bpf)
{
if (info->prog && (bpf->flags ^ info->flags) & XDP_FLAGS_MODES) {
NL_SET_ERR_MSG(bpf->extack,
"program loaded with different flags");
return false;
}
return true;
}
EXPORT_SYMBOL_GPL(xdp_attachment_flags_ok);
void xdp_attachment_setup(struct xdp_attachment_info *info,
struct netdev_bpf *bpf)
{
if (info->prog)
bpf_prog_put(info->prog);
info->prog = bpf->prog;
info->flags = bpf->flags;
}
EXPORT_SYMBOL_GPL(xdp_attachment_setup);

View File

@ -229,6 +229,7 @@ int inet_listen(struct socket *sock, int backlog)
err = inet_csk_listen_start(sk, backlog);
if (err)
goto out;
tcp_call_bpf(sk, BPF_SOCK_OPS_TCP_LISTEN_CB, 0, NULL);
}
sk->sk_max_ack_backlog = backlog;
err = 0;

View File

@ -134,7 +134,16 @@ bool parse_eth(struct ethhdr *eth, void *data_end,
return false;
eth_type = vlan_hdr->h_vlan_encapsulated_proto;
}
/* TODO: Handle double VLAN tagged packet */
/* Handle double VLAN tagged packet */
if (eth_type == htons(ETH_P_8021Q) || eth_type == htons(ETH_P_8021AD)) {
struct vlan_hdr *vlan_hdr;
vlan_hdr = (void *)eth + offset;
offset += sizeof(*vlan_hdr);
if ((void *)eth + offset > data_end)
return false;
eth_type = vlan_hdr->h_vlan_encapsulated_proto;
}
*eth_proto = ntohs(eth_type);
*l3_offset = offset;

View File

@ -0,0 +1,59 @@
ifndef allow-override
include ../scripts/Makefile.include
include ../scripts/utilities.mak
else
# Assume Makefile.helpers is being run from bpftool/Documentation
# subdirectory. Go up two more directories to fetch bpf.h header and
# associated script.
UP2DIR := ../../
endif
INSTALL ?= install
RM ?= rm -f
RMDIR ?= rmdir --ignore-fail-on-non-empty
ifeq ($(V),1)
Q =
else
Q = @
endif
prefix ?= /usr/local
mandir ?= $(prefix)/man
man7dir = $(mandir)/man7
HELPERS_RST = bpf-helpers.rst
MAN7_RST = $(HELPERS_RST)
_DOC_MAN7 = $(patsubst %.rst,%.7,$(MAN7_RST))
DOC_MAN7 = $(addprefix $(OUTPUT),$(_DOC_MAN7))
helpers: man7
man7: $(DOC_MAN7)
RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null)
$(OUTPUT)$(HELPERS_RST): $(UP2DIR)../../include/uapi/linux/bpf.h
$(QUIET_GEN)$(UP2DIR)../../scripts/bpf_helpers_doc.py --filename $< > $@
$(OUTPUT)%.7: $(OUTPUT)%.rst
ifndef RST2MAN_DEP
$(error "rst2man not found, but required to generate man pages")
endif
$(QUIET_GEN)rst2man $< > $@
helpers-clean:
$(call QUIET_CLEAN, eBPF_helpers-manpage)
$(Q)$(RM) $(DOC_MAN7) $(OUTPUT)$(HELPERS_RST)
helpers-install: helpers
$(call QUIET_INSTALL, eBPF_helpers-manpage)
$(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man7dir)
$(Q)$(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
helpers-uninstall:
$(call QUIET_UNINST, eBPF_helpers-manpage)
$(Q)$(RM) $(addprefix $(DESTDIR)$(man7dir)/,$(_DOC_MAN7))
$(Q)$(RMDIR) $(DESTDIR)$(man7dir)
.PHONY: helpers helpers-clean helpers-install helpers-uninstall

View File

@ -15,12 +15,15 @@ prefix ?= /usr/local
mandir ?= $(prefix)/man
man8dir = $(mandir)/man8
MAN8_RST = $(wildcard *.rst)
# Load targets for building eBPF helpers man page.
include ../../Makefile.helpers
MAN8_RST = $(filter-out $(HELPERS_RST),$(wildcard *.rst))
_DOC_MAN8 = $(patsubst %.rst,%.8,$(MAN8_RST))
DOC_MAN8 = $(addprefix $(OUTPUT),$(_DOC_MAN8))
man: man8
man: man8 helpers
man8: $(DOC_MAN8)
RST2MAN_DEP := $(shell command -v rst2man 2>/dev/null)
@ -31,16 +34,16 @@ ifndef RST2MAN_DEP
endif
$(QUIET_GEN)rst2man $< > $@
clean:
clean: helpers-clean
$(call QUIET_CLEAN, Documentation)
$(Q)$(RM) $(DOC_MAN8)
install: man
install: man helpers-install
$(call QUIET_INSTALL, Documentation-man)
$(Q)$(INSTALL) -d -m 755 $(DESTDIR)$(man8dir)
$(Q)$(INSTALL) -m 644 $(DOC_MAN8) $(DESTDIR)$(man8dir)
uninstall:
uninstall: helpers-uninstall
$(call QUIET_UNINST, Documentation-man)
$(Q)$(RM) $(addprefix $(DESTDIR)$(man8dir)/,$(_DOC_MAN8))
$(Q)$(RMDIR) $(DESTDIR)$(man8dir)

View File

@ -15,12 +15,13 @@ SYNOPSIS
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] | { **-f** | **--bpffs** } }
*COMMANDS* :=
{ **show** | **list** | **attach** | **detach** | **help** }
{ **show** | **list** | **tree** | **attach** | **detach** | **help** }
MAP COMMANDS
=============
| **bpftool** **cgroup { show | list }** *CGROUP*
| **bpftool** **cgroup tree** [*CGROUP_ROOT*]
| **bpftool** **cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
| **bpftool** **cgroup detach** *CGROUP* *ATTACH_TYPE* *PROG*
| **bpftool** **cgroup help**
@ -39,6 +40,15 @@ DESCRIPTION
Output will start with program ID followed by attach type,
attach flags and program name.
**bpftool cgroup tree** [*CGROUP_ROOT*]
Iterate over all cgroups in *CGROUP_ROOT* and list all
attached programs. If *CGROUP_ROOT* is not specified,
bpftool uses cgroup v2 mountpoint.
The output is similar to the output of cgroup show/list
commands: it starts with absolute cgroup path, followed by
program ID, attach type, attach flags and program name.
**bpftool cgroup attach** *CGROUP* *ATTACH_TYPE* *PROG* [*ATTACH_FLAGS*]
Attach program *PROG* to the cgroup *CGROUP* with attach type
*ATTACH_TYPE* and optional *ATTACH_FLAGS*.

View File

@ -24,10 +24,20 @@ MAP COMMANDS
| **bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual**}]
| **bpftool** **prog dump jited** *PROG* [{**file** *FILE* | **opcodes**}]
| **bpftool** **prog pin** *PROG* *FILE*
| **bpftool** **prog load** *OBJ* *FILE*
| **bpftool** **prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
| **bpftool** **prog help**
|
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
| *PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }
| *TYPE* := {
| **socket** | **kprobe** | **kretprobe** | **classifier** | **action** |
| **tracepoint** | **raw_tracepoint** | **xdp** | **perf_event** | **cgroup/skb** |
| **cgroup/sock** | **cgroup/dev** | **lwt_in** | **lwt_out** | **lwt_xmit** |
| **lwt_seg6local** | **sockops** | **sk_skb** | **sk_msg** | **lirc_mode2** |
| **cgroup/bind4** | **cgroup/bind6** | **cgroup/post_bind4** | **cgroup/post_bind6** |
| **cgroup/connect4** | **cgroup/connect6** | **cgroup/sendmsg4** | **cgroup/sendmsg6**
| }
DESCRIPTION
===========
@ -64,8 +74,19 @@ DESCRIPTION
Note: *FILE* must be located in *bpffs* mount.
**bpftool prog load** *OBJ* *FILE*
**bpftool prog load** *OBJ* *FILE* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*]
Load bpf program from binary *OBJ* and pin as *FILE*.
**type** is optional, if not specified program type will be
inferred from section names.
By default bpftool will create new maps as declared in the ELF
object being loaded. **map** parameter allows for the reuse
of existing maps. It can be specified multiple times, each
time for a different map. *IDX* refers to index of the map
to be replaced in the ELF file counting from 0, while *NAME*
allows to replace a map by name. *MAP* specifies the map to
use, referring to it by **id** or through a **pinned** file.
If **dev** *NAME* is specified program will be loaded onto
given networking device (offload).
Note: *FILE* must be located in *bpffs* mount.
@ -159,6 +180,14 @@ EXAMPLES
mov %rbx,0x0(%rbp)
48 89 5d 00
|
| **# bpftool prog load xdp1_kern.o /sys/fs/bpf/xdp1 type xdp map name rxcnt id 7**
| **# bpftool prog show pinned /sys/fs/bpf/xdp1**
| 9: xdp name xdp_prog1 tag 539ec6ce11b52f98 gpl
| loaded_at 2018-06-25T16:17:31-0700 uid 0
| xlated 488B jited 336B memlock 4096B map_ids 7
| **# rm /sys/fs/bpf/xdp1**
|
SEE ALSO
========

View File

@ -52,7 +52,7 @@ INSTALL ?= install
RM ?= rm -f
FEATURE_USER = .bpftool
FEATURE_TESTS = libbfd disassembler-four-args
FEATURE_TESTS = libbfd disassembler-four-args reallocarray
FEATURE_DISPLAY = libbfd disassembler-four-args
check_feat := 1
@ -75,6 +75,10 @@ ifeq ($(feature-disassembler-four-args), 1)
CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
endif
ifeq ($(feature-reallocarray), 0)
CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
endif
include $(wildcard $(OUTPUT)*.d)
all: $(OUTPUT)bpftool

View File

@ -99,6 +99,35 @@ _bpftool_get_prog_tags()
command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )
}
_bpftool_get_obj_map_names()
{
local obj
obj=$1
maps=$(objdump -j maps -t $obj 2>/dev/null | \
command awk '/g . maps/ {print $NF}')
COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) )
}
_bpftool_get_obj_map_idxs()
{
local obj
obj=$1
nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g . maps')
COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) )
}
_sysfs_get_netdevs()
{
COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \
"$cur" ) )
}
# For bpftool map update: retrieve type of the map to update.
_bpftool_map_update_map_type()
{
@ -214,12 +243,14 @@ _bpftool()
# Completion depends on object and command in use
case $object in
prog)
case $prev in
id)
_bpftool_get_prog_ids
return 0
;;
esac
if [[ $command != "load" ]]; then
case $prev in
id)
_bpftool_get_prog_ids
return 0
;;
esac
fi
local PROG_TYPE='id pinned tag'
case $command in
@ -262,8 +293,57 @@ _bpftool()
return 0
;;
load)
_filedir
return 0
local obj
if [[ ${#words[@]} -lt 6 ]]; then
_filedir
return 0
fi
obj=${words[3]}
if [[ ${words[-4]} == "map" ]]; then
COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )
return 0
fi
if [[ ${words[-3]} == "map" ]]; then
if [[ ${words[-2]} == "idx" ]]; then
_bpftool_get_obj_map_idxs $obj
elif [[ ${words[-2]} == "name" ]]; then
_bpftool_get_obj_map_names $obj
fi
return 0
fi
if [[ ${words[-2]} == "map" ]]; then
COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) )
return 0
fi
case $prev in
type)
COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier action tracepoint raw_tracepoint xdp perf_event cgroup/skb cgroup/sock cgroup/dev lwt_in lwt_out lwt_xmit lwt_seg6local sockops sk_skb sk_msg lirc_mode2 cgroup/bind4 cgroup/bind6 cgroup/connect4 cgroup/connect6 cgroup/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \
"$cur" ) )
return 0
;;
id)
_bpftool_get_map_ids
return 0
;;
pinned)
_filedir
return 0
;;
dev)
_sysfs_get_netdevs
return 0
;;
*)
COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
_bpftool_once_attr 'type'
_bpftool_once_attr 'dev'
return 0
;;
esac
;;
*)
[[ $prev == $object ]] && \
@ -414,6 +494,10 @@ _bpftool()
_filedir
return 0
;;
tree)
_filedir
return 0
;;
attach|detach)
local ATTACH_TYPES='ingress egress sock_create sock_ops \
device bind4 bind6 post_bind4 post_bind6 connect4 \
@ -455,7 +539,7 @@ _bpftool()
*)
[[ $prev == $object ]] && \
COMPREPLY=( $( compgen -W 'help attach detach \
show list' -- "$cur" ) )
show list tree' -- "$cur" ) )
;;
esac
;;

View File

@ -0,0 +1,251 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018 Facebook */
#include <ctype.h>
#include <stdio.h> /* for (FILE *) used by json_writer */
#include <string.h>
#include <asm/byteorder.h>
#include <linux/bitops.h>
#include <linux/btf.h>
#include <linux/err.h>
#include "btf.h"
#include "json_writer.h"
#include "main.h"
#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)
#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)
#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
#define BITS_ROUNDUP_BYTES(bits) \
(BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
__u8 bit_offset, const void *data);
static void btf_dumper_ptr(const void *data, json_writer_t *jw,
bool is_plain_text)
{
if (is_plain_text)
jsonw_printf(jw, "%p", *(unsigned long *)data);
else
jsonw_printf(jw, "%u", *(unsigned long *)data);
}
static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
int actual_type_id;
actual_type_id = btf__resolve_type(d->btf, type_id);
if (actual_type_id < 0)
return actual_type_id;
return btf_dumper_do_type(d, actual_type_id, 0, data);
}
static void btf_dumper_enum(const void *data, json_writer_t *jw)
{
jsonw_printf(jw, "%d", *(int *)data);
}
static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
const struct btf_type *t = btf__type_by_id(d->btf, type_id);
struct btf_array *arr = (struct btf_array *)(t + 1);
long long elem_size;
int ret = 0;
__u32 i;
elem_size = btf__resolve_size(d->btf, arr->type);
if (elem_size < 0)
return elem_size;
jsonw_start_array(d->jw);
for (i = 0; i < arr->nelems; i++) {
ret = btf_dumper_do_type(d, arr->type, 0,
data + i * elem_size);
if (ret)
break;
}
jsonw_end_array(d->jw);
return ret;
}
static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
const void *data, json_writer_t *jw,
bool is_plain_text)
{
int left_shift_bits, right_shift_bits;
int nr_bits = BTF_INT_BITS(int_type);
int total_bits_offset;
int bytes_to_copy;
int bits_to_copy;
__u64 print_num;
total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
bits_to_copy = bit_offset + nr_bits;
bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
print_num = 0;
memcpy(&print_num, data, bytes_to_copy);
#if defined(__BIG_ENDIAN_BITFIELD)
left_shift_bits = bit_offset;
#elif defined(__LITTLE_ENDIAN_BITFIELD)
left_shift_bits = 64 - bits_to_copy;
#else
#error neither big nor little endian
#endif
right_shift_bits = 64 - nr_bits;
print_num <<= left_shift_bits;
print_num >>= right_shift_bits;
if (is_plain_text)
jsonw_printf(jw, "0x%llx", print_num);
else
jsonw_printf(jw, "%llu", print_num);
}
static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
const void *data, json_writer_t *jw,
bool is_plain_text)
{
__u32 *int_type;
__u32 nr_bits;
int_type = (__u32 *)(t + 1);
nr_bits = BTF_INT_BITS(*int_type);
/* if this is bit field */
if (bit_offset || BTF_INT_OFFSET(*int_type) ||
BITS_PER_BYTE_MASKED(nr_bits)) {
btf_dumper_int_bits(*int_type, bit_offset, data, jw,
is_plain_text);
return 0;
}
switch (BTF_INT_ENCODING(*int_type)) {
case 0:
if (BTF_INT_BITS(*int_type) == 64)
jsonw_printf(jw, "%lu", *(__u64 *)data);
else if (BTF_INT_BITS(*int_type) == 32)
jsonw_printf(jw, "%u", *(__u32 *)data);
else if (BTF_INT_BITS(*int_type) == 16)
jsonw_printf(jw, "%hu", *(__u16 *)data);
else if (BTF_INT_BITS(*int_type) == 8)
jsonw_printf(jw, "%hhu", *(__u8 *)data);
else
btf_dumper_int_bits(*int_type, bit_offset, data, jw,
is_plain_text);
break;
case BTF_INT_SIGNED:
if (BTF_INT_BITS(*int_type) == 64)
jsonw_printf(jw, "%ld", *(long long *)data);
else if (BTF_INT_BITS(*int_type) == 32)
jsonw_printf(jw, "%d", *(int *)data);
else if (BTF_INT_BITS(*int_type) == 16)
jsonw_printf(jw, "%hd", *(short *)data);
else if (BTF_INT_BITS(*int_type) == 8)
jsonw_printf(jw, "%hhd", *(char *)data);
else
btf_dumper_int_bits(*int_type, bit_offset, data, jw,
is_plain_text);
break;
case BTF_INT_CHAR:
if (isprint(*(char *)data))
jsonw_printf(jw, "\"%c\"", *(char *)data);
else
if (is_plain_text)
jsonw_printf(jw, "0x%hhx", *(char *)data);
else
jsonw_printf(jw, "\"\\u00%02hhx\"",
*(char *)data);
break;
case BTF_INT_BOOL:
jsonw_bool(jw, *(int *)data);
break;
default:
/* shouldn't happen */
return -EINVAL;
}
return 0;
}
static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
const struct btf_type *t;
struct btf_member *m;
const void *data_off;
int ret = 0;
int i, vlen;
t = btf__type_by_id(d->btf, type_id);
if (!t)
return -EINVAL;
vlen = BTF_INFO_VLEN(t->info);
jsonw_start_object(d->jw);
m = (struct btf_member *)(t + 1);
for (i = 0; i < vlen; i++) {
data_off = data + BITS_ROUNDDOWN_BYTES(m[i].offset);
jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
ret = btf_dumper_do_type(d, m[i].type,
BITS_PER_BYTE_MASKED(m[i].offset),
data_off);
if (ret)
break;
}
jsonw_end_object(d->jw);
return ret;
}
static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
__u8 bit_offset, const void *data)
{
const struct btf_type *t = btf__type_by_id(d->btf, type_id);
switch (BTF_INFO_KIND(t->info)) {
case BTF_KIND_INT:
return btf_dumper_int(t, bit_offset, data, d->jw,
d->is_plain_text);
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
return btf_dumper_struct(d, type_id, data);
case BTF_KIND_ARRAY:
return btf_dumper_array(d, type_id, data);
case BTF_KIND_ENUM:
btf_dumper_enum(data, d->jw);
return 0;
case BTF_KIND_PTR:
btf_dumper_ptr(data, d->jw, d->is_plain_text);
return 0;
case BTF_KIND_UNKN:
jsonw_printf(d->jw, "(unknown)");
return 0;
case BTF_KIND_FWD:
/* map key or value can't be forward */
jsonw_printf(d->jw, "(fwd-kind-invalid)");
return -EINVAL;
case BTF_KIND_TYPEDEF:
case BTF_KIND_VOLATILE:
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
return btf_dumper_modifier(d, type_id, data);
default:
jsonw_printf(d->jw, "(unsupported-kind");
return -EINVAL;
}
}
int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
const void *data)
{
return btf_dumper_do_type(d, type_id, 0, data);
}

View File

@ -2,7 +2,12 @@
// Copyright (C) 2017 Facebook
// Author: Roman Gushchin <guro@fb.com>
#define _XOPEN_SOURCE 500
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <mntent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
@ -53,7 +58,8 @@ static enum bpf_attach_type parse_attach_type(const char *str)
}
static int show_bpf_prog(int id, const char *attach_type_str,
const char *attach_flags_str)
const char *attach_flags_str,
int level)
{
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
@ -78,7 +84,8 @@ static int show_bpf_prog(int id, const char *attach_type_str,
jsonw_string_field(json_wtr, "name", info.name);
jsonw_end_object(json_wtr);
} else {
printf("%-8u %-15s %-15s %-15s\n", info.id,
printf("%s%-8u %-15s %-15s %-15s\n", level ? " " : "",
info.id,
attach_type_str,
attach_flags_str,
info.name);
@ -88,7 +95,20 @@ static int show_bpf_prog(int id, const char *attach_type_str,
return 0;
}
static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
{
__u32 prog_cnt = 0;
int ret;
ret = bpf_prog_query(cgroup_fd, type, 0, NULL, NULL, &prog_cnt);
if (ret)
return -1;
return prog_cnt;
}
static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
int level)
{
__u32 prog_ids[1024] = {0};
char *attach_flags_str;
@ -123,7 +143,7 @@ static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
for (iter = 0; iter < prog_cnt; iter++)
show_bpf_prog(prog_ids[iter], attach_type_strings[type],
attach_flags_str);
attach_flags_str, level);
return 0;
}
@ -161,7 +181,7 @@ static int do_show(int argc, char **argv)
* If we were able to get the show for at least one
* attach type, let's return 0.
*/
if (show_attached_bpf_progs(cgroup_fd, type) == 0)
if (show_attached_bpf_progs(cgroup_fd, type, 0) == 0)
ret = 0;
}
@ -173,6 +193,143 @@ static int do_show(int argc, char **argv)
return ret;
}
/*
* To distinguish nftw() errors and do_show_tree_fn() errors
* and avoid duplicating error messages, let's return -2
* from do_show_tree_fn() in case of error.
*/
#define NFTW_ERR -1
#define SHOW_TREE_FN_ERR -2
static int do_show_tree_fn(const char *fpath, const struct stat *sb,
int typeflag, struct FTW *ftw)
{
enum bpf_attach_type type;
bool skip = true;
int cgroup_fd;
if (typeflag != FTW_D)
return 0;
cgroup_fd = open(fpath, O_RDONLY);
if (cgroup_fd < 0) {
p_err("can't open cgroup %s: %s", fpath, strerror(errno));
return SHOW_TREE_FN_ERR;
}
for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
int count = count_attached_bpf_progs(cgroup_fd, type);
if (count < 0 && errno != EINVAL) {
p_err("can't query bpf programs attached to %s: %s",
fpath, strerror(errno));
close(cgroup_fd);
return SHOW_TREE_FN_ERR;
}
if (count > 0) {
skip = false;
break;
}
}
if (skip) {
close(cgroup_fd);
return 0;
}
if (json_output) {
jsonw_start_object(json_wtr);
jsonw_string_field(json_wtr, "cgroup", fpath);
jsonw_name(json_wtr, "programs");
jsonw_start_array(json_wtr);
} else {
printf("%s\n", fpath);
}
for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
show_attached_bpf_progs(cgroup_fd, type, ftw->level);
if (json_output) {
jsonw_end_array(json_wtr);
jsonw_end_object(json_wtr);
}
close(cgroup_fd);
return 0;
}
static char *find_cgroup_root(void)
{
struct mntent *mnt;
FILE *f;
f = fopen("/proc/mounts", "r");
if (f == NULL)
return NULL;
while ((mnt = getmntent(f))) {
if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
fclose(f);
return strdup(mnt->mnt_dir);
}
}
fclose(f);
return NULL;
}
static int do_show_tree(int argc, char **argv)
{
char *cgroup_root;
int ret;
switch (argc) {
case 0:
cgroup_root = find_cgroup_root();
if (!cgroup_root) {
p_err("cgroup v2 isn't mounted");
return -1;
}
break;
case 1:
cgroup_root = argv[0];
break;
default:
p_err("too many parameters for cgroup tree");
return -1;
}
if (json_output)
jsonw_start_array(json_wtr);
else
printf("%s\n"
"%-8s %-15s %-15s %-15s\n",
"CgroupPath",
"ID", "AttachType", "AttachFlags", "Name");
switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
case NFTW_ERR:
p_err("can't iterate over %s: %s", cgroup_root,
strerror(errno));
ret = -1;
break;
case SHOW_TREE_FN_ERR:
ret = -1;
break;
default:
ret = 0;
}
if (json_output)
jsonw_end_array(json_wtr);
if (argc == 0)
free(cgroup_root);
return ret;
}
static int do_attach(int argc, char **argv)
{
enum bpf_attach_type attach_type;
@ -289,6 +446,7 @@ static int do_help(int argc, char **argv)
fprintf(stderr,
"Usage: %s %s { show | list } CGROUP\n"
" %s %s tree [CGROUP_ROOT]\n"
" %s %s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
" %s %s detach CGROUP ATTACH_TYPE PROG\n"
" %s %s help\n"
@ -298,6 +456,7 @@ static int do_help(int argc, char **argv)
" " HELP_SPEC_PROGRAM "\n"
" " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2],
bin_name, argv[-2], bin_name, argv[-2],
bin_name, argv[-2], bin_name, argv[-2]);
@ -307,6 +466,7 @@ static int do_help(int argc, char **argv)
static const struct cmd cmds[] = {
{ "show", do_show },
{ "list", do_show },
{ "tree", do_show_tree },
{ "attach", do_attach },
{ "detach", do_detach },
{ "help", do_help },

View File

@ -42,6 +42,7 @@
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/hashtable.h>
#include <tools/libc_compat.h>
#include "json_writer.h"
@ -50,6 +51,21 @@
#define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); })
#define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
#define BAD_ARG() ({ p_err("what is '%s'?", *argv); -1; })
#define GET_ARG() ({ argc--; *argv++; })
#define REQ_ARGS(cnt) \
({ \
int _cnt = (cnt); \
bool _res; \
\
if (argc < _cnt) { \
p_err("'%s' needs at least %d arguments, %d found", \
argv[-1], _cnt, argc); \
_res = false; \
} else { \
_res = true; \
} \
_res; \
})
#define ERR_MAX_LEN 1024
@ -59,6 +75,8 @@
"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
#define HELP_SPEC_OPTIONS \
"OPTIONS := { {-j|--json} [{-p|--pretty}] | {-f|--bpffs} }"
#define HELP_SPEC_MAP \
"MAP := { id MAP_ID | pinned FILE }"
enum bpf_obj_type {
BPF_OBJ_UNKNOWN,
@ -120,6 +138,7 @@ int do_cgroup(int argc, char **arg);
int do_perf(int argc, char **arg);
int prog_parse_fd(int *argc, char ***argv);
int map_parse_fd(int *argc, char ***argv);
int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len);
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes,
@ -131,4 +150,19 @@ unsigned int get_page_size(void);
unsigned int get_possible_cpus(void);
const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino);
struct btf_dumper {
const struct btf *btf;
json_writer_t *jw;
bool is_plain_text;
};
/* btf_dumper_type - print data along with type information
* @d: an instance containing context for dumping types
* @type_id: index in btf->types array. this points to the type to be dumped
* @data: pointer the actual data, i.e. the values to be printed
*
* Returns zero on success and negative error code otherwise
*/
int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
const void *data);
#endif

View File

@ -34,6 +34,7 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/err.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -44,6 +45,8 @@
#include <bpf.h>
#include "btf.h"
#include "json_writer.h"
#include "main.h"
static const char * const map_type_name[] = {
@ -93,7 +96,7 @@ static void *alloc_value(struct bpf_map_info *info)
return malloc(info->value_size);
}
static int map_parse_fd(int *argc, char ***argv)
int map_parse_fd(int *argc, char ***argv)
{
int fd;
@ -148,8 +151,109 @@ int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
return fd;
}
static int do_dump_btf(const struct btf_dumper *d,
struct bpf_map_info *map_info, void *key,
void *value)
{
int ret;
/* start of key-value pair */
jsonw_start_object(d->jw);
jsonw_name(d->jw, "key");
ret = btf_dumper_type(d, map_info->btf_key_type_id, key);
if (ret)
goto err_end_obj;
jsonw_name(d->jw, "value");
ret = btf_dumper_type(d, map_info->btf_value_type_id, value);
err_end_obj:
/* end of key-value pair */
jsonw_end_object(d->jw);
return ret;
}
static int get_btf(struct bpf_map_info *map_info, struct btf **btf)
{
struct bpf_btf_info btf_info = { 0 };
__u32 len = sizeof(btf_info);
__u32 last_size;
int btf_fd;
void *ptr;
int err;
err = 0;
*btf = NULL;
btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id);
if (btf_fd < 0)
return 0;
/* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
* let's start with a sane default - 4KiB here - and resize it only if
* bpf_obj_get_info_by_fd() needs a bigger buffer.
*/
btf_info.btf_size = 4096;
last_size = btf_info.btf_size;
ptr = malloc(last_size);
if (!ptr) {
err = -ENOMEM;
goto exit_free;
}
bzero(ptr, last_size);
btf_info.btf = ptr_to_u64(ptr);
err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
if (!err && btf_info.btf_size > last_size) {
void *temp_ptr;
last_size = btf_info.btf_size;
temp_ptr = realloc(ptr, last_size);
if (!temp_ptr) {
err = -ENOMEM;
goto exit_free;
}
ptr = temp_ptr;
bzero(ptr, last_size);
btf_info.btf = ptr_to_u64(ptr);
err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
}
if (err || btf_info.btf_size > last_size) {
err = errno;
goto exit_free;
}
*btf = btf__new((__u8 *)btf_info.btf, btf_info.btf_size, NULL);
if (IS_ERR(*btf)) {
err = PTR_ERR(btf);
*btf = NULL;
}
exit_free:
close(btf_fd);
free(ptr);
return err;
}
static json_writer_t *get_btf_writer(void)
{
json_writer_t *jw = jsonw_new(stdout);
if (!jw)
return NULL;
jsonw_pretty(jw, true);
return jw;
}
static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
unsigned char *value)
unsigned char *value, struct btf *btf)
{
jsonw_start_object(json_wtr);
@ -158,6 +262,16 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
print_hex_data_json(key, info->key_size);
jsonw_name(json_wtr, "value");
print_hex_data_json(value, info->value_size);
if (btf) {
struct btf_dumper d = {
.btf = btf,
.jw = json_wtr,
.is_plain_text = false,
};
jsonw_name(json_wtr, "formatted");
do_dump_btf(&d, info, key, value);
}
} else {
unsigned int i, n;
@ -508,10 +622,12 @@ static int do_show(int argc, char **argv)
static int do_dump(int argc, char **argv)
{
struct bpf_map_info info = {};
void *key, *value, *prev_key;
unsigned int num_elems = 0;
struct bpf_map_info info = {};
__u32 len = sizeof(info);
json_writer_t *btf_wtr;
struct btf *btf = NULL;
int err;
int fd;
@ -537,8 +653,27 @@ static int do_dump(int argc, char **argv)
}
prev_key = NULL;
err = get_btf(&info, &btf);
if (err) {
p_err("failed to get btf");
goto exit_free;
}
if (json_output)
jsonw_start_array(json_wtr);
else
if (btf) {
btf_wtr = get_btf_writer();
if (!btf_wtr) {
p_info("failed to create json writer for btf. falling back to plain output");
btf__free(btf);
btf = NULL;
} else {
jsonw_start_array(btf_wtr);
}
}
while (true) {
err = bpf_map_get_next_key(fd, prev_key, key);
if (err) {
@ -549,9 +684,19 @@ static int do_dump(int argc, char **argv)
if (!bpf_map_lookup_elem(fd, key, value)) {
if (json_output)
print_entry_json(&info, key, value);
print_entry_json(&info, key, value, btf);
else
print_entry_plain(&info, key, value);
if (btf) {
struct btf_dumper d = {
.btf = btf,
.jw = btf_wtr,
.is_plain_text = true,
};
do_dump_btf(&d, &info, key, value);
} else {
print_entry_plain(&info, key, value);
}
} else {
if (json_output) {
jsonw_name(json_wtr, "key");
@ -574,14 +719,19 @@ static int do_dump(int argc, char **argv)
if (json_output)
jsonw_end_array(json_wtr);
else
else if (btf) {
jsonw_end_array(btf_wtr);
jsonw_destroy(&btf_wtr);
} else {
printf("Found %u element%s\n", num_elems,
num_elems != 1 ? "s" : "");
}
exit_free:
free(key);
free(value);
close(fd);
btf__free(btf);
return err;
}
@ -637,6 +787,8 @@ static int do_lookup(int argc, char **argv)
{
struct bpf_map_info info = {};
__u32 len = sizeof(info);
json_writer_t *btf_wtr;
struct btf *btf = NULL;
void *key, *value;
int err;
int fd;
@ -661,27 +813,60 @@ static int do_lookup(int argc, char **argv)
goto exit_free;
err = bpf_map_lookup_elem(fd, key, value);
if (!err) {
if (json_output)
print_entry_json(&info, key, value);
else
print_entry_plain(&info, key, value);
} else if (errno == ENOENT) {
if (json_output) {
jsonw_null(json_wtr);
if (err) {
if (errno == ENOENT) {
if (json_output) {
jsonw_null(json_wtr);
} else {
printf("key:\n");
fprint_hex(stdout, key, info.key_size, " ");
printf("\n\nNot found\n");
}
} else {
printf("key:\n");
fprint_hex(stdout, key, info.key_size, " ");
printf("\n\nNot found\n");
p_err("lookup failed: %s", strerror(errno));
}
goto exit_free;
}
/* here means bpf_map_lookup_elem() succeeded */
err = get_btf(&info, &btf);
if (err) {
p_err("failed to get btf");
goto exit_free;
}
if (json_output) {
print_entry_json(&info, key, value, btf);
} else if (btf) {
/* if here json_wtr wouldn't have been initialised,
* so let's create separate writer for btf
*/
btf_wtr = get_btf_writer();
if (!btf_wtr) {
p_info("failed to create json writer for btf. falling back to plain output");
btf__free(btf);
btf = NULL;
print_entry_plain(&info, key, value);
} else {
struct btf_dumper d = {
.btf = btf,
.jw = btf_wtr,
.is_plain_text = true,
};
do_dump_btf(&d, &info, key, value);
jsonw_destroy(&btf_wtr);
}
} else {
p_err("lookup failed: %s", strerror(errno));
print_entry_plain(&info, key, value);
}
exit_free:
free(key);
free(value);
close(fd);
btf__free(btf);
return err;
}
@ -824,7 +1009,7 @@ static int do_help(int argc, char **argv)
" %s %s event_pipe MAP [cpu N index M]\n"
" %s %s help\n"
"\n"
" MAP := { id MAP_ID | pinned FILE }\n"
" " HELP_SPEC_MAP "\n"
" DATA := { [hex] BYTES }\n"
" " HELP_SPEC_PROGRAM "\n"
" VALUE := { DATA | MAP | PROG }\n"

View File

@ -31,6 +31,7 @@
* SOFTWARE.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
@ -39,9 +40,12 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/err.h>
#include <bpf.h>
#include <libbpf.h>
@ -679,31 +683,247 @@ static int do_pin(int argc, char **argv)
return err;
}
struct map_replace {
int idx;
int fd;
char *name;
};
int map_replace_compar(const void *p1, const void *p2)
{
const struct map_replace *a = p1, *b = p2;
return a->idx - b->idx;
}
static int do_load(int argc, char **argv)
{
enum bpf_attach_type expected_attach_type;
struct bpf_object_open_attr attr = {
.prog_type = BPF_PROG_TYPE_UNSPEC,
};
struct map_replace *map_replace = NULL;
unsigned int old_map_fds = 0;
struct bpf_program *prog;
struct bpf_object *obj;
int prog_fd;
struct bpf_map *map;
const char *pinfile;
unsigned int i, j;
__u32 ifindex = 0;
int idx, err;
if (argc != 2)
usage();
if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
p_err("failed to load program");
if (!REQ_ARGS(2))
return -1;
attr.file = GET_ARG();
pinfile = GET_ARG();
while (argc) {
if (is_prefix(*argv, "type")) {
char *type;
NEXT_ARG();
if (attr.prog_type != BPF_PROG_TYPE_UNSPEC) {
p_err("program type already specified");
goto err_free_reuse_maps;
}
if (!REQ_ARGS(1))
goto err_free_reuse_maps;
/* Put a '/' at the end of type to appease libbpf */
type = malloc(strlen(*argv) + 2);
if (!type) {
p_err("mem alloc failed");
goto err_free_reuse_maps;
}
*type = 0;
strcat(type, *argv);
strcat(type, "/");
err = libbpf_prog_type_by_name(type, &attr.prog_type,
&expected_attach_type);
free(type);
if (err < 0) {
p_err("unknown program type '%s'", *argv);
goto err_free_reuse_maps;
}
NEXT_ARG();
} else if (is_prefix(*argv, "map")) {
char *endptr, *name;
int fd;
NEXT_ARG();
if (!REQ_ARGS(4))
goto err_free_reuse_maps;
if (is_prefix(*argv, "idx")) {
NEXT_ARG();
idx = strtoul(*argv, &endptr, 0);
if (*endptr) {
p_err("can't parse %s as IDX", *argv);
goto err_free_reuse_maps;
}
name = NULL;
} else if (is_prefix(*argv, "name")) {
NEXT_ARG();
name = *argv;
idx = -1;
} else {
p_err("expected 'idx' or 'name', got: '%s'?",
*argv);
goto err_free_reuse_maps;
}
NEXT_ARG();
fd = map_parse_fd(&argc, &argv);
if (fd < 0)
goto err_free_reuse_maps;
map_replace = reallocarray(map_replace, old_map_fds + 1,
sizeof(*map_replace));
if (!map_replace) {
p_err("mem alloc failed");
goto err_free_reuse_maps;
}
map_replace[old_map_fds].idx = idx;
map_replace[old_map_fds].name = name;
map_replace[old_map_fds].fd = fd;
old_map_fds++;
} else if (is_prefix(*argv, "dev")) {
NEXT_ARG();
if (ifindex) {
p_err("offload device already specified");
goto err_free_reuse_maps;
}
if (!REQ_ARGS(1))
goto err_free_reuse_maps;
ifindex = if_nametoindex(*argv);
if (!ifindex) {
p_err("unrecognized netdevice '%s': %s",
*argv, strerror(errno));
goto err_free_reuse_maps;
}
NEXT_ARG();
} else {
p_err("expected no more arguments, 'type', 'map' or 'dev', got: '%s'?",
*argv);
goto err_free_reuse_maps;
}
}
if (do_pin_fd(prog_fd, argv[1]))
obj = bpf_object__open_xattr(&attr);
if (IS_ERR_OR_NULL(obj)) {
p_err("failed to open object file");
goto err_free_reuse_maps;
}
prog = bpf_program__next(NULL, obj);
if (!prog) {
p_err("object file doesn't contain any bpf program");
goto err_close_obj;
}
bpf_program__set_ifindex(prog, ifindex);
if (attr.prog_type == BPF_PROG_TYPE_UNSPEC) {
const char *sec_name = bpf_program__title(prog, false);
err = libbpf_prog_type_by_name(sec_name, &attr.prog_type,
&expected_attach_type);
if (err < 0) {
p_err("failed to guess program type based on section name %s\n",
sec_name);
goto err_close_obj;
}
}
bpf_program__set_type(prog, attr.prog_type);
bpf_program__set_expected_attach_type(prog, expected_attach_type);
qsort(map_replace, old_map_fds, sizeof(*map_replace),
map_replace_compar);
/* After the sort maps by name will be first on the list, because they
* have idx == -1. Resolve them.
*/
j = 0;
while (j < old_map_fds && map_replace[j].name) {
i = 0;
bpf_map__for_each(map, obj) {
if (!strcmp(bpf_map__name(map), map_replace[j].name)) {
map_replace[j].idx = i;
break;
}
i++;
}
if (map_replace[j].idx == -1) {
p_err("unable to find map '%s'", map_replace[j].name);
goto err_close_obj;
}
j++;
}
/* Resort if any names were resolved */
if (j)
qsort(map_replace, old_map_fds, sizeof(*map_replace),
map_replace_compar);
/* Set ifindex and name reuse */
j = 0;
idx = 0;
bpf_map__for_each(map, obj) {
if (!bpf_map__is_offload_neutral(map))
bpf_map__set_ifindex(map, ifindex);
if (j < old_map_fds && idx == map_replace[j].idx) {
err = bpf_map__reuse_fd(map, map_replace[j++].fd);
if (err) {
p_err("unable to set up map reuse: %d", err);
goto err_close_obj;
}
/* Next reuse wants to apply to the same map */
if (j < old_map_fds && map_replace[j].idx == idx) {
p_err("replacement for map idx %d specified more than once",
idx);
goto err_close_obj;
}
}
idx++;
}
if (j < old_map_fds) {
p_err("map idx '%d' not used", map_replace[j].idx);
goto err_close_obj;
}
err = bpf_object__load(obj);
if (err) {
p_err("failed to load object file");
goto err_close_obj;
}
if (do_pin_fd(bpf_program__fd(prog), pinfile))
goto err_close_obj;
if (json_output)
jsonw_null(json_wtr);
bpf_object__close(obj);
for (i = 0; i < old_map_fds; i++)
close(map_replace[i].fd);
free(map_replace);
return 0;
err_close_obj:
bpf_object__close(obj);
err_free_reuse_maps:
for (i = 0; i < old_map_fds; i++)
close(map_replace[i].fd);
free(map_replace);
return -1;
}
@ -719,10 +939,19 @@ static int do_help(int argc, char **argv)
" %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
" %s %s dump jited PROG [{ file FILE | opcodes }]\n"
" %s %s pin PROG FILE\n"
" %s %s load OBJ FILE\n"
" %s %s load OBJ FILE [type TYPE] [dev NAME] \\\n"
" [map { idx IDX | name NAME } MAP]\n"
" %s %s help\n"
"\n"
" " HELP_SPEC_MAP "\n"
" " HELP_SPEC_PROGRAM "\n"
" TYPE := { socket | kprobe | kretprobe | classifier | action |\n"
" tracepoint | raw_tracepoint | xdp | perf_event | cgroup/skb |\n"
" cgroup/sock | cgroup/dev | lwt_in | lwt_out | lwt_xmit |\n"
" lwt_seg6local | sockops | sk_skb | sk_msg | lirc_mode2 |\n"
" cgroup/bind4 | cgroup/bind6 | cgroup/post_bind4 |\n"
" cgroup/post_bind6 | cgroup/connect4 | cgroup/connect6 |\n"
" cgroup/sendmsg4 | cgroup/sendmsg6 }\n"
" " HELP_SPEC_OPTIONS "\n"
"",
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],

View File

@ -35,6 +35,7 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@ -66,9 +67,8 @@ void kernel_syms_load(struct dump_data *dd)
while (!feof(fp)) {
if (!fgets(buff, sizeof(buff), fp))
break;
tmp = realloc(dd->sym_mapping,
(dd->sym_count + 1) *
sizeof(*dd->sym_mapping));
tmp = reallocarray(dd->sym_mapping, dd->sym_count + 1,
sizeof(*dd->sym_mapping));
if (!tmp) {
out:
free(dd->sym_mapping);

View File

@ -57,6 +57,7 @@ FEATURE_TESTS_BASIC := \
libunwind-aarch64 \
pthread-attr-setaffinity-np \
pthread-barrier \
reallocarray \
stackprotector-all \
timerfd \
libdw-dwarf-unwind \

View File

@ -14,6 +14,7 @@ FILES= \
test-libaudit.bin \
test-libbfd.bin \
test-disassembler-four-args.bin \
test-reallocarray.bin \
test-liberty.bin \
test-liberty-z.bin \
test-cplus-demangle.bin \
@ -204,6 +205,9 @@ $(OUTPUT)test-libbfd.bin:
$(OUTPUT)test-disassembler-four-args.bin:
$(BUILD) -DPACKAGE='"perf"' -lbfd -lopcodes
$(OUTPUT)test-reallocarray.bin:
$(BUILD)
$(OUTPUT)test-liberty.bin:
$(CC) $(CFLAGS) -Wall -Werror -o $@ test-libbfd.c -DPACKAGE='"perf"' $(LDFLAGS) -lbfd -ldl -liberty

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <stdlib.h>
int main(void)
{
return !!reallocarray(NULL, 1, 1);
}

View File

@ -36,3 +36,7 @@
#endif
#define __printf(a, b) __attribute__((format(printf, a, b)))
#define __scanf(a, b) __attribute__((format(scanf, a, b)))
#if GCC_VERSION >= 50100
#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1
#endif

View File

@ -0,0 +1,278 @@
/* SPDX-License-Identifier: GPL-2.0 OR MIT */
#ifndef __LINUX_OVERFLOW_H
#define __LINUX_OVERFLOW_H
#include <linux/compiler.h>
/*
* In the fallback code below, we need to compute the minimum and
* maximum values representable in a given type. These macros may also
* be useful elsewhere, so we provide them outside the
* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW block.
*
* It would seem more obvious to do something like
*
* #define type_min(T) (T)(is_signed_type(T) ? (T)1 << (8*sizeof(T)-1) : 0)
* #define type_max(T) (T)(is_signed_type(T) ? ((T)1 << (8*sizeof(T)-1)) - 1 : ~(T)0)
*
* Unfortunately, the middle expressions, strictly speaking, have
* undefined behaviour, and at least some versions of gcc warn about
* the type_max expression (but not if -fsanitize=undefined is in
* effect; in that case, the warning is deferred to runtime...).
*
* The slightly excessive casting in type_min is to make sure the
* macros also produce sensible values for the exotic type _Bool. [The
* overflow checkers only almost work for _Bool, but that's
* a-feature-not-a-bug, since people shouldn't be doing arithmetic on
* _Bools. Besides, the gcc builtins don't allow _Bool* as third
* argument.]
*
* Idea stolen from
* https://mail-index.netbsd.org/tech-misc/2007/02/05/0000.html -
* credit to Christian Biere.
*/
#define is_signed_type(type) (((type)(-1)) < (type)1)
#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type)))
#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
#define type_min(T) ((T)((T)-type_max(T)-(T)1))
#ifdef COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW
/*
* For simplicity and code hygiene, the fallback code below insists on
* a, b and *d having the same type (similar to the min() and max()
* macros), whereas gcc's type-generic overflow checkers accept
* different types. Hence we don't just make check_add_overflow an
* alias for __builtin_add_overflow, but add type checks similar to
* below.
*/
#define check_add_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
__builtin_add_overflow(__a, __b, __d); \
})
#define check_sub_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
__builtin_sub_overflow(__a, __b, __d); \
})
#define check_mul_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
__builtin_mul_overflow(__a, __b, __d); \
})
#else
/* Checking for unsigned overflow is relatively easy without causing UB. */
#define __unsigned_add_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
*__d = __a + __b; \
*__d < __a; \
})
#define __unsigned_sub_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
*__d = __a - __b; \
__a < __b; \
})
/*
* If one of a or b is a compile-time constant, this avoids a division.
*/
#define __unsigned_mul_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
*__d = __a * __b; \
__builtin_constant_p(__b) ? \
__b > 0 && __a > type_max(typeof(__a)) / __b : \
__a > 0 && __b > type_max(typeof(__b)) / __a; \
})
/*
* For signed types, detecting overflow is much harder, especially if
* we want to avoid UB. But the interface of these macros is such that
* we must provide a result in *d, and in fact we must produce the
* result promised by gcc's builtins, which is simply the possibly
* wrapped-around value. Fortunately, we can just formally do the
* operations in the widest relevant unsigned type (u64) and then
* truncate the result - gcc is smart enough to generate the same code
* with and without the (u64) casts.
*/
/*
* Adding two signed integers can overflow only if they have the same
* sign, and overflow has happened iff the result has the opposite
* sign.
*/
#define __signed_add_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
*__d = (u64)__a + (u64)__b; \
(((~(__a ^ __b)) & (*__d ^ __a)) \
& type_min(typeof(__a))) != 0; \
})
/*
* Subtraction is similar, except that overflow can now happen only
* when the signs are opposite. In this case, overflow has happened if
* the result has the opposite sign of a.
*/
#define __signed_sub_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
*__d = (u64)__a - (u64)__b; \
((((__a ^ __b)) & (*__d ^ __a)) \
& type_min(typeof(__a))) != 0; \
})
/*
* Signed multiplication is rather hard. gcc always follows C99, so
* division is truncated towards 0. This means that we can write the
* overflow check like this:
*
* (a > 0 && (b > MAX/a || b < MIN/a)) ||
* (a < -1 && (b > MIN/a || b < MAX/a) ||
* (a == -1 && b == MIN)
*
* The redundant casts of -1 are to silence an annoying -Wtype-limits
* (included in -Wextra) warning: When the type is u8 or u16, the
* __b_c_e in check_mul_overflow obviously selects
* __unsigned_mul_overflow, but unfortunately gcc still parses this
* code and warns about the limited range of __b.
*/
#define __signed_mul_overflow(a, b, d) ({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
typeof(a) __tmax = type_max(typeof(a)); \
typeof(a) __tmin = type_min(typeof(a)); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
*__d = (u64)__a * (u64)__b; \
(__b > 0 && (__a > __tmax/__b || __a < __tmin/__b)) || \
(__b < (typeof(__b))-1 && (__a > __tmin/__b || __a < __tmax/__b)) || \
(__b == (typeof(__b))-1 && __a == __tmin); \
})
#define check_add_overflow(a, b, d) \
__builtin_choose_expr(is_signed_type(typeof(a)), \
__signed_add_overflow(a, b, d), \
__unsigned_add_overflow(a, b, d))
#define check_sub_overflow(a, b, d) \
__builtin_choose_expr(is_signed_type(typeof(a)), \
__signed_sub_overflow(a, b, d), \
__unsigned_sub_overflow(a, b, d))
#define check_mul_overflow(a, b, d) \
__builtin_choose_expr(is_signed_type(typeof(a)), \
__signed_mul_overflow(a, b, d), \
__unsigned_mul_overflow(a, b, d))
#endif /* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */
/**
* array_size() - Calculate size of 2-dimensional array.
*
* @a: dimension one
* @b: dimension two
*
* Calculates size of 2-dimensional array: @a * @b.
*
* Returns: number of bytes needed to represent the array or SIZE_MAX on
* overflow.
*/
static inline __must_check size_t array_size(size_t a, size_t b)
{
size_t bytes;
if (check_mul_overflow(a, b, &bytes))
return SIZE_MAX;
return bytes;
}
/**
* array3_size() - Calculate size of 3-dimensional array.
*
* @a: dimension one
* @b: dimension two
* @c: dimension three
*
* Calculates size of 3-dimensional array: @a * @b * @c.
*
* Returns: number of bytes needed to represent the array or SIZE_MAX on
* overflow.
*/
static inline __must_check size_t array3_size(size_t a, size_t b, size_t c)
{
size_t bytes;
if (check_mul_overflow(a, b, &bytes))
return SIZE_MAX;
if (check_mul_overflow(bytes, c, &bytes))
return SIZE_MAX;
return bytes;
}
static inline __must_check size_t __ab_c_size(size_t n, size_t size, size_t c)
{
size_t bytes;
if (check_mul_overflow(n, size, &bytes))
return SIZE_MAX;
if (check_add_overflow(bytes, c, &bytes))
return SIZE_MAX;
return bytes;
}
/**
* struct_size() - Calculate size of structure with trailing array.
* @p: Pointer to the structure.
* @member: Name of the array member.
* @n: Number of elements in the array.
*
* Calculates size of memory needed for structure @p followed by an
* array of @n @member elements.
*
* Return: number of bytes needed or SIZE_MAX on overflow.
*/
#define struct_size(p, member, n) \
__ab_c_size(n, \
sizeof(*(p)->member) + __must_be_array((p)->member),\
sizeof(*(p)))
#endif /* __LINUX_OVERFLOW_H */

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (C) 2018 Netronome Systems, Inc. */
#ifndef __TOOLS_LIBC_COMPAT_H
#define __TOOLS_LIBC_COMPAT_H
#include <stdlib.h>
#include <linux/overflow.h>
#ifdef COMPAT_NEED_REALLOCARRAY
static inline void *reallocarray(void *ptr, size_t nmemb, size_t size)
{
size_t bytes;
if (unlikely(check_mul_overflow(nmemb, size, &bytes)))
return NULL;
return realloc(ptr, bytes);
}
#endif
#endif

View File

@ -1826,7 +1826,7 @@ union bpf_attr {
* A non-negative value equal to or less than *size* on success,
* or a negative error in case of failure.
*
* int skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header)
* int bpf_skb_load_bytes_relative(const struct sk_buff *skb, u32 offset, void *to, u32 len, u32 start_header)
* Description
* This helper is similar to **bpf_skb_load_bytes**\ () in that
* it provides an easy way to load *len* bytes from *offset*
@ -1857,7 +1857,8 @@ union bpf_attr {
* is resolved), the nexthop address is returned in ipv4_dst
* or ipv6_dst based on family, smac is set to mac address of
* egress device, dmac is set to nexthop mac address, rt_metric
* is set to metric from route (IPv4/IPv6 only).
* is set to metric from route (IPv4/IPv6 only), and ifindex
* is set to the device index of the nexthop from the FIB lookup.
*
* *plen* argument is the size of the passed in struct.
* *flags* argument can be a combination of one or more of the
@ -1873,9 +1874,10 @@ union bpf_attr {
* *ctx* is either **struct xdp_md** for XDP programs or
* **struct sk_buff** tc cls_act programs.
* Return
* Egress device index on success, 0 if packet needs to continue
* up the stack for further processing or a negative error in case
* of failure.
* * < 0 if any input argument is invalid
* * 0 on success (packet is forwarded, nexthop neighbor exists)
* * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
* packet is not forwarded or needs assist from full stack
*
* int bpf_sock_hash_update(struct bpf_sock_ops_kern *skops, struct bpf_map *map, void *key, u64 flags)
* Description
@ -2031,7 +2033,6 @@ union bpf_attr {
* This helper is only available is the kernel was compiled with
* the **CONFIG_BPF_LIRC_MODE2** configuration option set to
* "**y**".
*
* Return
* 0
*
@ -2051,7 +2052,6 @@ union bpf_attr {
* This helper is only available is the kernel was compiled with
* the **CONFIG_BPF_LIRC_MODE2** configuration option set to
* "**y**".
*
* Return
* 0
*
@ -2555,6 +2555,9 @@ enum {
* Arg1: old_state
* Arg2: new_state
*/
BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after
* socket transition to LISTEN state.
*/
};
/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
@ -2612,6 +2615,18 @@ struct bpf_raw_tracepoint_args {
#define BPF_FIB_LOOKUP_DIRECT BIT(0)
#define BPF_FIB_LOOKUP_OUTPUT BIT(1)
enum {
BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */
BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */
BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */
BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */
BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */
BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */
BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */
BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */
BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */
};
struct bpf_fib_lookup {
/* input: network family for lookup (AF_INET, AF_INET6)
* output: network family of egress nexthop
@ -2625,7 +2640,11 @@ struct bpf_fib_lookup {
/* total length of packet from network header - used for MTU check */
__u16 tot_len;
__u32 ifindex; /* L3 device index for lookup */
/* input: L3 device index for lookup
* output: device index from FIB lookup
*/
__u32 ifindex;
union {
/* inputs to lookup */

View File

@ -1 +1 @@
libbpf-y := libbpf.o bpf.o nlattr.o btf.o
libbpf-y := libbpf.o bpf.o nlattr.o btf.o libbpf_errno.o

View File

@ -66,7 +66,7 @@ ifndef VERBOSE
endif
FEATURE_USER = .libbpf
FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf
FEATURE_TESTS = libelf libelf-getphdrnum libelf-mmap bpf reallocarray
FEATURE_DISPLAY = libelf bpf
INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi -I$(srctree)/tools/perf
@ -120,6 +120,10 @@ ifeq ($(feature-libelf-getphdrnum), 1)
override CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT
endif
ifeq ($(feature-reallocarray), 0)
override CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
endif
# Append required CFLAGS
override CFLAGS += $(EXTRA_WARNINGS)
override CFLAGS += -Werror -Wall

View File

@ -17,6 +17,11 @@
#define BTF_MAX_NR_TYPES 65535
#define IS_MODIFIER(k) (((k) == BTF_KIND_TYPEDEF) || \
((k) == BTF_KIND_VOLATILE) || \
((k) == BTF_KIND_CONST) || \
((k) == BTF_KIND_RESTRICT))
static struct btf_type btf_void;
struct btf {
@ -33,14 +38,6 @@ struct btf {
int fd;
};
static const char *btf_name_by_offset(const struct btf *btf, uint32_t offset)
{
if (offset < btf->hdr->str_len)
return &btf->strings[offset];
else
return NULL;
}
static int btf_add_type(struct btf *btf, struct btf_type *t)
{
if (btf->types_size - btf->nr_types < 2) {
@ -190,15 +187,6 @@ static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
return 0;
}
static const struct btf_type *btf_type_by_id(const struct btf *btf,
uint32_t type_id)
{
if (type_id > btf->nr_types)
return NULL;
return btf->types[type_id];
}
static bool btf_type_is_void(const struct btf_type *t)
{
return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
@ -234,7 +222,7 @@ int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id)
int64_t size = -1;
int i;
t = btf_type_by_id(btf, type_id);
t = btf__type_by_id(btf, type_id);
for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
i++) {
size = btf_type_size(t);
@ -259,7 +247,7 @@ int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id)
return -EINVAL;
}
t = btf_type_by_id(btf, type_id);
t = btf__type_by_id(btf, type_id);
}
if (size < 0)
@ -271,6 +259,26 @@ int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id)
return nelems * size;
}
int btf__resolve_type(const struct btf *btf, __u32 type_id)
{
const struct btf_type *t;
int depth = 0;
t = btf__type_by_id(btf, type_id);
while (depth < MAX_RESOLVE_DEPTH &&
!btf_type_is_void_or_null(t) &&
IS_MODIFIER(BTF_INFO_KIND(t->info))) {
type_id = t->type;
t = btf__type_by_id(btf, type_id);
depth++;
}
if (depth == MAX_RESOLVE_DEPTH || btf_type_is_void_or_null(t))
return -EINVAL;
return type_id;
}
int32_t btf__find_by_name(const struct btf *btf, const char *type_name)
{
uint32_t i;
@ -280,7 +288,7 @@ int32_t btf__find_by_name(const struct btf *btf, const char *type_name)
for (i = 1; i <= btf->nr_types; i++) {
const struct btf_type *t = btf->types[i];
const char *name = btf_name_by_offset(btf, t->name_off);
const char *name = btf__name_by_offset(btf, t->name_off);
if (name && !strcmp(type_name, name))
return i;
@ -371,3 +379,20 @@ int btf__fd(const struct btf *btf)
{
return btf->fd;
}
const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
{
if (offset < btf->hdr->str_len)
return &btf->strings[offset];
else
return NULL;
}
const struct btf_type *btf__type_by_id(const struct btf *btf,
__u32 type_id)
{
if (type_id > btf->nr_types)
return NULL;
return btf->types[type_id];
}

View File

@ -17,6 +17,9 @@ void btf__free(struct btf *btf);
struct btf *btf__new(uint8_t *data, uint32_t size, btf_print_fn_t err_log);
int32_t btf__find_by_name(const struct btf *btf, const char *type_name);
int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id);
int btf__resolve_type(const struct btf *btf, __u32 type_id);
int btf__fd(const struct btf *btf);
const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id);
#endif

View File

@ -22,6 +22,7 @@
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
@ -41,6 +42,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <tools/libc_compat.h>
#include <libelf.h>
#include <gelf.h>
@ -95,54 +97,6 @@ void libbpf_set_print(libbpf_print_fn_t warn,
#define STRERR_BUFSIZE 128
#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START)
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c)
#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
static const char *libbpf_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
[ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
[ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost",
[ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch",
[ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf",
[ERRCODE_OFFSET(RELOC)] = "Relocation failed",
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
[ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type",
[ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message",
[ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence",
};
int libbpf_strerror(int err, char *buf, size_t size)
{
if (!buf || !size)
return -1;
err = err > 0 ? err : -err;
if (err < __LIBBPF_ERRNO__START) {
int ret;
ret = strerror_r(err, buf, size);
buf[size - 1] = '\0';
return ret;
}
if (err < __LIBBPF_ERRNO__END) {
const char *msg;
msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
snprintf(buf, size, "%s", msg);
buf[size - 1] = '\0';
return 0;
}
snprintf(buf, size, "Unknown libbpf error %d", err);
buf[size - 1] = '\0';
return -1;
}
#define CHECK_ERR(action, err, out) do { \
err = action; \
if (err) \
@ -369,7 +323,7 @@ bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
progs = obj->programs;
nr_progs = obj->nr_programs;
progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1));
progs = reallocarray(progs, nr_progs + 1, sizeof(progs[0]));
if (!progs) {
/*
* In this case the original obj->programs
@ -870,8 +824,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
continue;
}
reloc = realloc(reloc,
sizeof(*obj->efile.reloc) * nr_reloc);
reloc = reallocarray(reloc, nr_reloc,
sizeof(*obj->efile.reloc));
if (!reloc) {
pr_warning("realloc failed\n");
err = -ENOMEM;
@ -1081,6 +1035,53 @@ static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
return 0;
}
int bpf_map__reuse_fd(struct bpf_map *map, int fd)
{
struct bpf_map_info info = {};
__u32 len = sizeof(info);
int new_fd, err;
char *new_name;
err = bpf_obj_get_info_by_fd(fd, &info, &len);
if (err)
return err;
new_name = strdup(info.name);
if (!new_name)
return -errno;
new_fd = open("/", O_RDONLY | O_CLOEXEC);
if (new_fd < 0)
goto err_free_new_name;
new_fd = dup3(fd, new_fd, O_CLOEXEC);
if (new_fd < 0)
goto err_close_new_fd;
err = zclose(map->fd);
if (err)
goto err_close_new_fd;
free(map->name);
map->fd = new_fd;
map->name = new_name;
map->def.type = info.type;
map->def.key_size = info.key_size;
map->def.value_size = info.value_size;
map->def.max_entries = info.max_entries;
map->def.map_flags = info.map_flags;
map->btf_key_type_id = info.btf_key_type_id;
map->btf_value_type_id = info.btf_value_type_id;
return 0;
err_close_new_fd:
close(new_fd);
err_free_new_name:
free(new_name);
return -errno;
}
static int
bpf_object__create_maps(struct bpf_object *obj)
{
@ -1093,6 +1094,12 @@ bpf_object__create_maps(struct bpf_object *obj)
struct bpf_map_def *def = &map->def;
int *pfd = &map->fd;
if (map->fd >= 0) {
pr_debug("skip map create (preset) %s: fd=%d\n",
map->name, map->fd);
continue;
}
create_attr.name = map->name;
create_attr.map_ifindex = map->map_ifindex;
create_attr.map_type = def->type;
@ -1163,7 +1170,7 @@ bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
return -LIBBPF_ERRNO__RELOC;
}
new_cnt = prog->insns_cnt + text->insns_cnt;
new_insn = realloc(prog->insns, new_cnt * sizeof(*insn));
new_insn = reallocarray(prog->insns, new_cnt, sizeof(*insn));
if (!new_insn) {
pr_warning("oom in prog realloc\n");
return -ENOMEM;
@ -1520,15 +1527,26 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz,
return ERR_PTR(err);
}
struct bpf_object *bpf_object__open(const char *path)
struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr)
{
/* param validation */
if (!path)
if (!attr->file)
return NULL;
pr_debug("loading %s\n", path);
pr_debug("loading %s\n", attr->file);
return __bpf_object__open(path, NULL, 0, true);
return __bpf_object__open(attr->file, NULL, 0,
bpf_prog_type__needs_kver(attr->prog_type));
}
struct bpf_object *bpf_object__open(const char *path)
{
struct bpf_object_open_attr attr = {
.file = path,
.prog_type = BPF_PROG_TYPE_UNSPEC,
};
return bpf_object__open_xattr(&attr);
}
struct bpf_object *bpf_object__open_buffer(void *obj_buf,
@ -2081,23 +2099,31 @@ static const struct {
#undef BPF_S_PROG_SEC
#undef BPF_SA_PROG_SEC
static int bpf_program__identify_section(struct bpf_program *prog)
int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
enum bpf_attach_type *expected_attach_type)
{
int i;
if (!prog->section_name)
goto err;
if (!name)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(section_names); i++)
if (strncmp(prog->section_name, section_names[i].sec,
section_names[i].len) == 0)
return i;
for (i = 0; i < ARRAY_SIZE(section_names); i++) {
if (strncmp(name, section_names[i].sec, section_names[i].len))
continue;
*prog_type = section_names[i].prog_type;
*expected_attach_type = section_names[i].expected_attach_type;
return 0;
}
return -EINVAL;
}
err:
pr_warning("failed to guess program type based on section name %s\n",
prog->section_name);
return -1;
static int
bpf_program__identify_section(struct bpf_program *prog,
enum bpf_prog_type *prog_type,
enum bpf_attach_type *expected_attach_type)
{
return libbpf_prog_type_by_name(prog->section_name, prog_type,
expected_attach_type);
}
int bpf_map__fd(struct bpf_map *map)
@ -2146,6 +2172,11 @@ void *bpf_map__priv(struct bpf_map *map)
return map ? map->priv : ERR_PTR(-EINVAL);
}
bool bpf_map__is_offload_neutral(struct bpf_map *map)
{
return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
}
void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
{
map->map_ifindex = ifindex;
@ -2225,12 +2256,15 @@ int bpf_prog_load(const char *file, enum bpf_prog_type type,
int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
struct bpf_object **pobj, int *prog_fd)
{
struct bpf_object_open_attr open_attr = {
.file = attr->file,
.prog_type = attr->prog_type,
};
struct bpf_program *prog, *first_prog = NULL;
enum bpf_attach_type expected_attach_type;
enum bpf_prog_type prog_type;
struct bpf_object *obj;
struct bpf_map *map;
int section_idx;
int err;
if (!attr)
@ -2238,8 +2272,7 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
if (!attr->file)
return -EINVAL;
obj = __bpf_object__open(attr->file, NULL, 0,
bpf_prog_type__needs_kver(attr->prog_type));
obj = bpf_object__open_xattr(&open_attr);
if (IS_ERR_OR_NULL(obj))
return -ENOENT;
@ -2252,14 +2285,14 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
prog->prog_ifindex = attr->ifindex;
expected_attach_type = attr->expected_attach_type;
if (prog_type == BPF_PROG_TYPE_UNSPEC) {
section_idx = bpf_program__identify_section(prog);
if (section_idx < 0) {
err = bpf_program__identify_section(prog, &prog_type,
&expected_attach_type);
if (err < 0) {
pr_warning("failed to guess program type based on section name %s\n",
prog->section_name);
bpf_object__close(obj);
return -EINVAL;
}
prog_type = section_names[section_idx].prog_type;
expected_attach_type =
section_names[section_idx].expected_attach_type;
}
bpf_program__set_type(prog, prog_type);
@ -2271,7 +2304,8 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
}
bpf_map__for_each(map, obj) {
map->map_ifindex = attr->ifindex;
if (!bpf_map__is_offload_neutral(map))
map->map_ifindex = attr->ifindex;
}
if (!first_prog) {

View File

@ -66,7 +66,13 @@ void libbpf_set_print(libbpf_print_fn_t warn,
/* Hide internal to user */
struct bpf_object;
struct bpf_object_open_attr {
const char *file;
enum bpf_prog_type prog_type;
};
struct bpf_object *bpf_object__open(const char *path);
struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr);
struct bpf_object *bpf_object__open_buffer(void *obj_buf,
size_t obj_buf_sz,
const char *name);
@ -92,6 +98,9 @@ int bpf_object__set_priv(struct bpf_object *obj, void *priv,
bpf_object_clear_priv_t clear_priv);
void *bpf_object__priv(struct bpf_object *prog);
int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
enum bpf_attach_type *expected_attach_type);
/* Accessors of bpf_program */
struct bpf_program;
struct bpf_program *bpf_program__next(struct bpf_program *prog,
@ -252,6 +261,8 @@ typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
int bpf_map__set_priv(struct bpf_map *map, void *priv,
bpf_map_clear_priv_t clear_priv);
void *bpf_map__priv(struct bpf_map *map);
int bpf_map__reuse_fd(struct bpf_map *map, int fd);
bool bpf_map__is_offload_neutral(struct bpf_map *map);
void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
int bpf_map__pin(struct bpf_map *map, const char *path);

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2015 Huawei Inc.
* Copyright (C) 2017 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
#include <stdio.h>
#include <string.h>
#include "libbpf.h"
#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START)
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c)
#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
static const char *libbpf_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
[ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
[ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost",
[ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch",
[ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf",
[ERRCODE_OFFSET(RELOC)] = "Relocation failed",
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
[ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type",
[ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message",
[ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence",
};
int libbpf_strerror(int err, char *buf, size_t size)
{
if (!buf || !size)
return -1;
err = err > 0 ? err : -err;
if (err < __LIBBPF_ERRNO__START) {
int ret;
ret = strerror_r(err, buf, size);
buf[size - 1] = '\0';
return ret;
}
if (err < __LIBBPF_ERRNO__END) {
const char *msg;
msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
snprintf(buf, size, "%s", msg);
buf[size - 1] = '\0';
return 0;
}
snprintf(buf, size, "Unknown libbpf error %d", err);
buf[size - 1] = '\0';
return -1;
}

View File

@ -61,6 +61,7 @@ $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
$(OUTPUT)/test_sock: cgroup_helpers.c
$(OUTPUT)/test_sock_addr: cgroup_helpers.c
$(OUTPUT)/test_sockmap: cgroup_helpers.c
$(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
$(OUTPUT)/test_progs: trace_helpers.c
$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c

View File

@ -118,7 +118,7 @@ static int join_cgroup_from_top(char *cgroup_path)
*
* On success, it returns 0, otherwise on failure it returns 1.
*/
int join_cgroup(char *path)
int join_cgroup(const char *path)
{
char cgroup_path[PATH_MAX + 1];
@ -158,7 +158,7 @@ void cleanup_cgroup_environment(void)
* On success, it returns the file descriptor. On failure it returns 0.
* If there is a failure, it prints the error to stderr.
*/
int create_and_get_cgroup(char *path)
int create_and_get_cgroup(const char *path)
{
char cgroup_path[PATH_MAX + 1];
int fd;
@ -186,7 +186,7 @@ int create_and_get_cgroup(char *path)
* which is an invalid cgroup id.
* If there is a failure, it prints the error to stderr.
*/
unsigned long long get_cgroup_id(char *path)
unsigned long long get_cgroup_id(const char *path)
{
int dirfd, err, flags, mount_id, fhsize;
union {

View File

@ -9,10 +9,10 @@
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
int create_and_get_cgroup(char *path);
int join_cgroup(char *path);
int create_and_get_cgroup(const char *path);
int join_cgroup(const char *path);
int setup_cgroup_environment(void);
void cleanup_cgroup_environment(void);
unsigned long long get_cgroup_id(char *path);
unsigned long long get_cgroup_id(const char *path);
#endif

View File

@ -339,6 +339,11 @@ class NetdevSim:
self.dfs = DebugfsDir(self.dfs_dir)
return self.dfs
def dfs_read(self, f):
path = os.path.join(self.dfs_dir, f)
_, data = cmd('cat %s' % (path))
return data.strip()
def dfs_num_bound_progs(self):
path = os.path.join(self.dfs_dir, "bpf_bound_progs")
_, progs = cmd('ls %s' % (path))
@ -547,11 +552,11 @@ def check_extack(output, reference, args):
if skip_extack:
return
lines = output.split("\n")
comp = len(lines) >= 2 and lines[1] == reference
comp = len(lines) >= 2 and lines[1] == 'Error: ' + reference
fail(not comp, "Missing or incorrect netlink extack message")
def check_extack_nsim(output, reference, args):
check_extack(output, "Error: netdevsim: " + reference, args)
check_extack(output, "netdevsim: " + reference, args)
def check_no_extack(res, needle):
fail((res[1] + res[2]).count(needle) or (res[1] + res[2]).count("Warning:"),
@ -654,7 +659,7 @@ try:
ret, _, err = sim.cls_bpf_add_filter(obj, skip_sw=True,
fail=False, include_stderr=True)
fail(ret == 0, "TC filter loaded without enabling TC offloads")
check_extack(err, "Error: TC offload is disabled on net device.", args)
check_extack(err, "TC offload is disabled on net device.", args)
sim.wait_for_flush()
sim.set_ethtool_tc_offloads(True)
@ -694,7 +699,7 @@ try:
skip_sw=True,
fail=False, include_stderr=True)
fail(ret == 0, "Offloaded a filter to chain other than 0")
check_extack(err, "Error: Driver supports only offload of chain 0.", args)
check_extack(err, "Driver supports only offload of chain 0.", args)
sim.tc_flush_filters()
start_test("Test TC replace...")
@ -814,24 +819,20 @@ try:
"Device parameters reported for non-offloaded program")
start_test("Test XDP prog replace with bad flags...")
ret, _, err = sim.set_xdp(obj, "offload", force=True,
ret, _, err = sim.set_xdp(obj, "generic", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Replaced XDP program with a program in different mode")
check_extack_nsim(err, "program loaded with different flags.", args)
fail(err.count("File exists") != 1, "Replaced driver XDP with generic")
ret, _, err = sim.set_xdp(obj, "", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Replaced XDP program with a program in different mode")
check_extack_nsim(err, "program loaded with different flags.", args)
check_extack(err, "program loaded with different flags.", args)
start_test("Test XDP prog remove with bad flags...")
ret, _, err = sim.unset_xdp("offload", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Removed program with a bad mode mode")
check_extack_nsim(err, "program loaded with different flags.", args)
ret, _, err = sim.unset_xdp("", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Removed program with a bad mode mode")
check_extack_nsim(err, "program loaded with different flags.", args)
fail(ret == 0, "Removed program with a bad mode")
check_extack(err, "program loaded with different flags.", args)
start_test("Test MTU restrictions...")
ret, _ = sim.set_mtu(9000, fail=False)
@ -891,6 +892,60 @@ try:
rm(pin_file)
bpftool_prog_list_wait(expected=0)
start_test("Test multi-attachment XDP - attach...")
sim.set_xdp(obj, "offload")
xdp = sim.ip_link_show(xdp=True)["xdp"]
offloaded = sim.dfs_read("bpf_offloaded_id")
fail("prog" not in xdp, "Base program not reported in single program mode")
fail(len(ipl["xdp"]["attached"]) != 1,
"Wrong attached program count with one program")
sim.set_xdp(obj, "")
two_xdps = sim.ip_link_show(xdp=True)["xdp"]
offloaded2 = sim.dfs_read("bpf_offloaded_id")
fail(two_xdps["mode"] != 4, "Bad mode reported with multiple programs")
fail("prog" in two_xdps, "Base program reported in multi program mode")
fail(xdp["attached"][0] not in two_xdps["attached"],
"Offload program not reported after driver activated")
fail(len(two_xdps["attached"]) != 2,
"Wrong attached program count with two programs")
fail(two_xdps["attached"][0]["prog"]["id"] ==
two_xdps["attached"][1]["prog"]["id"],
"offloaded and drv programs have the same id")
fail(offloaded != offloaded2,
"offload ID changed after loading driver program")
start_test("Test multi-attachment XDP - replace...")
ret, _, err = sim.set_xdp(obj, "offload", fail=False, include_stderr=True)
fail(err.count("busy") != 1, "Replaced one of programs without -force")
start_test("Test multi-attachment XDP - detach...")
ret, _, err = sim.unset_xdp("drv", force=True,
fail=False, include_stderr=True)
fail(ret == 0, "Removed program with a bad mode")
check_extack(err, "program loaded with different flags.", args)
sim.unset_xdp("offload")
xdp = sim.ip_link_show(xdp=True)["xdp"]
offloaded = sim.dfs_read("bpf_offloaded_id")
fail(xdp["mode"] != 1, "Bad mode reported after multiple programs")
fail("prog" not in xdp,
"Base program not reported after multi program mode")
fail(xdp["attached"][0] not in two_xdps["attached"],
"Offload program not reported after driver activated")
fail(len(ipl["xdp"]["attached"]) != 1,
"Wrong attached program count with remaining programs")
fail(offloaded != "0", "offload ID reported with only driver program left")
start_test("Test multi-attachment XDP - device remove...")
sim.set_xdp(obj, "offload")
sim.remove()
sim = NetdevSim()
sim.set_ethtool_tc_offloads(True)
start_test("Test mixing of TC and XDP...")
sim.tc_add_ingress()
sim.set_xdp(obj, "offload")

View File

@ -12,5 +12,6 @@ struct tcpbpf_globals {
__u32 good_cb_test_rv;
__u64 bytes_received;
__u64 bytes_acked;
__u32 num_listen;
};
#endif

View File

@ -96,15 +96,22 @@ int bpf_testcb(struct bpf_sock_ops *skops)
if (!gp)
break;
g = *gp;
g.total_retrans = skops->total_retrans;
g.data_segs_in = skops->data_segs_in;
g.data_segs_out = skops->data_segs_out;
g.bytes_received = skops->bytes_received;
g.bytes_acked = skops->bytes_acked;
if (skops->args[0] == BPF_TCP_LISTEN) {
g.num_listen++;
} else {
g.total_retrans = skops->total_retrans;
g.data_segs_in = skops->data_segs_in;
g.data_segs_out = skops->data_segs_out;
g.bytes_received = skops->bytes_received;
g.bytes_acked = skops->bytes_acked;
}
bpf_map_update_elem(&global_map, &key, &g,
BPF_ANY);
}
break;
case BPF_SOCK_OPS_TCP_LISTEN_CB:
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG);
break;
default:
rv = -1;
}

View File

@ -1,27 +1,59 @@
// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <assert.h>
#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/bpf.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "bpf_util.h"
#include "bpf_rlimit.h"
#include <linux/perf_event.h>
#include "bpf_util.h"
#include "cgroup_helpers.h"
#include "test_tcpbpf.h"
#define EXPECT_EQ(expected, actual, fmt) \
do { \
if ((expected) != (actual)) { \
printf(" Value of: " #actual "\n" \
" Actual: %" fmt "\n" \
" Expected: %" fmt "\n", \
(actual), (expected)); \
goto err; \
} \
} while (0)
int verify_result(const struct tcpbpf_globals *result)
{
__u32 expected_events;
expected_events = ((1 << BPF_SOCK_OPS_TIMEOUT_INIT) |
(1 << BPF_SOCK_OPS_RWND_INIT) |
(1 << BPF_SOCK_OPS_TCP_CONNECT_CB) |
(1 << BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) |
(1 << BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB) |
(1 << BPF_SOCK_OPS_NEEDS_ECN) |
(1 << BPF_SOCK_OPS_STATE_CB) |
(1 << BPF_SOCK_OPS_TCP_LISTEN_CB));
EXPECT_EQ(expected_events, result->event_map, "#" PRIx32);
EXPECT_EQ(501ULL, result->bytes_received, "llu");
EXPECT_EQ(1002ULL, result->bytes_acked, "llu");
EXPECT_EQ(1, result->data_segs_in, PRIu32);
EXPECT_EQ(1, result->data_segs_out, PRIu32);
EXPECT_EQ(0x80, result->bad_cb_test_rv, PRIu32);
EXPECT_EQ(0, result->good_cb_test_rv, PRIu32);
EXPECT_EQ(1, result->num_listen, PRIu32);
return 0;
err:
return -1;
}
static int bpf_find_map(const char *test, struct bpf_object *obj,
const char *name)
{
@ -35,42 +67,28 @@ static int bpf_find_map(const char *test, struct bpf_object *obj,
return bpf_map__fd(map);
}
#define SYSTEM(CMD) \
do { \
if (system(CMD)) { \
printf("system(%s) FAILS!\n", CMD); \
} \
} while (0)
int main(int argc, char **argv)
{
const char *file = "test_tcpbpf_kern.o";
struct tcpbpf_globals g = {0};
int cg_fd, prog_fd, map_fd;
bool debug_flag = false;
const char *cg_path = "/foo";
int error = EXIT_FAILURE;
struct bpf_object *obj;
char cmd[100], *dir;
struct stat buffer;
int prog_fd, map_fd;
int cg_fd = -1;
__u32 key = 0;
int pid;
int rv;
if (argc > 1 && strcmp(argv[1], "-d") == 0)
debug_flag = true;
if (setup_cgroup_environment())
goto err;
dir = "/tmp/cgroupv2/foo";
cg_fd = create_and_get_cgroup(cg_path);
if (!cg_fd)
goto err;
if (stat(dir, &buffer) != 0) {
SYSTEM("mkdir -p /tmp/cgroupv2");
SYSTEM("mount -t cgroup2 none /tmp/cgroupv2");
SYSTEM("mkdir -p /tmp/cgroupv2/foo");
}
pid = (int) getpid();
sprintf(cmd, "echo %d >> /tmp/cgroupv2/foo/cgroup.procs", pid);
SYSTEM(cmd);
if (join_cgroup(cg_path))
goto err;
cg_fd = open(dir, O_DIRECTORY, O_RDONLY);
if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) {
printf("FAILED: load_bpf_file failed for: %s\n", file);
goto err;
@ -83,7 +101,10 @@ int main(int argc, char **argv)
goto err;
}
SYSTEM("./tcp_server.py");
if (system("./tcp_server.py")) {
printf("FAILED: TCP server\n");
goto err;
}
map_fd = bpf_find_map(__func__, obj, "global_map");
if (map_fd < 0)
@ -95,34 +116,16 @@ int main(int argc, char **argv)
goto err;
}
if (g.bytes_received != 501 || g.bytes_acked != 1002 ||
g.data_segs_in != 1 || g.data_segs_out != 1 ||
(g.event_map ^ 0x47e) != 0 || g.bad_cb_test_rv != 0x80 ||
g.good_cb_test_rv != 0) {
if (verify_result(&g)) {
printf("FAILED: Wrong stats\n");
if (debug_flag) {
printf("\n");
printf("bytes_received: %d (expecting 501)\n",
(int)g.bytes_received);
printf("bytes_acked: %d (expecting 1002)\n",
(int)g.bytes_acked);
printf("data_segs_in: %d (expecting 1)\n",
g.data_segs_in);
printf("data_segs_out: %d (expecting 1)\n",
g.data_segs_out);
printf("event_map: 0x%x (at least 0x47e)\n",
g.event_map);
printf("bad_cb_test_rv: 0x%x (expecting 0x80)\n",
g.bad_cb_test_rv);
printf("good_cb_test_rv:0x%x (expecting 0)\n",
g.good_cb_test_rv);
}
goto err;
}
printf("PASSED!\n");
error = 0;
err:
bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
close(cg_fd);
cleanup_cgroup_environment();
return error;
}