Skip to content

Commit 27b29f6

Browse files
Alexei Starovoitovdavem330
Alexei Starovoitov
authored andcommitted
bpf: add bpf_redirect() helper
Existing bpf_clone_redirect() helper clones skb before redirecting it to RX or TX of destination netdev. Introduce bpf_redirect() helper that does that without cloning. Benchmarked with two hosts using 10G ixgbe NICs. One host is doing line rate pktgen. Another host is configured as: $ tc qdisc add dev $dev ingress $ tc filter add dev $dev root pref 10 u32 match u32 0 0 flowid 1:2 \ action bpf run object-file tcbpf1_kern.o section clone_redirect_xmit drop so it receives the packet on $dev and immediately xmits it on $dev + 1 The section 'clone_redirect_xmit' in tcbpf1_kern.o file has the program that does bpf_clone_redirect() and performance is 2.0 Mpps $ tc filter add dev $dev root pref 10 u32 match u32 0 0 flowid 1:2 \ action bpf run object-file tcbpf1_kern.o section redirect_xmit drop which is using bpf_redirect() - 2.4 Mpps and using cls_bpf with integrated actions as: $ tc filter add dev $dev root pref 10 \ bpf run object-file tcbpf1_kern.o section redirect_xmit integ_act classid 1 performance is 2.5 Mpps To summarize: u32+act_bpf using clone_redirect - 2.0 Mpps u32+act_bpf using redirect - 2.4 Mpps cls_bpf using redirect - 2.5 Mpps For comparison linux bridge in this setup is doing 2.1 Mpps and ixgbe rx + drop in ip_rcv - 7.8 Mpps Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Daniel Borkmann <[email protected]> Acked-by: John Fastabend <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 045efa8 commit 27b29f6

File tree

9 files changed

+91
-1
lines changed

9 files changed

+91
-1
lines changed

include/net/sch_generic.h

+1
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ void __qdisc_calculate_pkt_len(struct sk_buff *skb,
402402
const struct qdisc_size_table *stab);
403403
bool tcf_destroy(struct tcf_proto *tp, bool force);
404404
void tcf_destroy_chain(struct tcf_proto __rcu **fl);
405+
int skb_do_redirect(struct sk_buff *);
405406

406407
/* Reset all TX qdiscs greater then index of a device. */
407408
static inline void qdisc_reset_all_tx_gt(struct net_device *dev, unsigned int i)

include/uapi/linux/bpf.h

+8
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,14 @@ enum bpf_func_id {
272272
BPF_FUNC_skb_get_tunnel_key,
273273
BPF_FUNC_skb_set_tunnel_key,
274274
BPF_FUNC_perf_event_read, /* u64 bpf_perf_event_read(&map, index) */
275+
/**
276+
* bpf_redirect(ifindex, flags) - redirect to another netdev
277+
* @ifindex: ifindex of the net device
278+
* @flags: bit 0 - if set, redirect to ingress instead of egress
279+
* other bits - reserved
280+
* Return: TC_ACT_REDIRECT
281+
*/
282+
BPF_FUNC_redirect,
275283
__BPF_FUNC_MAX_ID,
276284
};
277285

include/uapi/linux/pkt_cls.h

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ enum {
8787
#define TC_ACT_STOLEN 4
8888
#define TC_ACT_QUEUED 5
8989
#define TC_ACT_REPEAT 6
90+
#define TC_ACT_REDIRECT 7
9091
#define TC_ACT_JUMP 0x10000000
9192

9293
/* Action type identifiers*/

net/core/dev.c

+8
Original file line numberDiff line numberDiff line change
@@ -3670,6 +3670,14 @@ static inline struct sk_buff *handle_ing(struct sk_buff *skb,
36703670
case TC_ACT_QUEUED:
36713671
kfree_skb(skb);
36723672
return NULL;
3673+
case TC_ACT_REDIRECT:
3674+
/* skb_mac_header check was done by cls/act_bpf, so
3675+
* we can safely push the L2 header back before
3676+
* redirecting to another netdev
3677+
*/
3678+
__skb_push(skb, skb->mac_len);
3679+
skb_do_redirect(skb);
3680+
return NULL;
36733681
default:
36743682
break;
36753683
}

net/core/filter.c

+44
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,48 @@ const struct bpf_func_proto bpf_clone_redirect_proto = {
14271427
.arg3_type = ARG_ANYTHING,
14281428
};
14291429

1430+
struct redirect_info {
1431+
u32 ifindex;
1432+
u32 flags;
1433+
};
1434+
1435+
static DEFINE_PER_CPU(struct redirect_info, redirect_info);
1436+
static u64 bpf_redirect(u64 ifindex, u64 flags, u64 r3, u64 r4, u64 r5)
1437+
{
1438+
struct redirect_info *ri = this_cpu_ptr(&redirect_info);
1439+
1440+
ri->ifindex = ifindex;
1441+
ri->flags = flags;
1442+
return TC_ACT_REDIRECT;
1443+
}
1444+
1445+
int skb_do_redirect(struct sk_buff *skb)
1446+
{
1447+
struct redirect_info *ri = this_cpu_ptr(&redirect_info);
1448+
struct net_device *dev;
1449+
1450+
dev = dev_get_by_index_rcu(dev_net(skb->dev), ri->ifindex);
1451+
ri->ifindex = 0;
1452+
if (unlikely(!dev)) {
1453+
kfree_skb(skb);
1454+
return -EINVAL;
1455+
}
1456+
1457+
if (BPF_IS_REDIRECT_INGRESS(ri->flags))
1458+
return dev_forward_skb(dev, skb);
1459+
1460+
skb->dev = dev;
1461+
return dev_queue_xmit(skb);
1462+
}
1463+
1464+
const struct bpf_func_proto bpf_redirect_proto = {
1465+
.func = bpf_redirect,
1466+
.gpl_only = false,
1467+
.ret_type = RET_INTEGER,
1468+
.arg1_type = ARG_ANYTHING,
1469+
.arg2_type = ARG_ANYTHING,
1470+
};
1471+
14301472
static u64 bpf_get_cgroup_classid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
14311473
{
14321474
return task_get_classid((struct sk_buff *) (unsigned long) r1);
@@ -1607,6 +1649,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)
16071649
return &bpf_skb_get_tunnel_key_proto;
16081650
case BPF_FUNC_skb_set_tunnel_key:
16091651
return bpf_get_skb_set_tunnel_key_proto();
1652+
case BPF_FUNC_redirect:
1653+
return &bpf_redirect_proto;
16101654
default:
16111655
return sk_filter_func_proto(func_id);
16121656
}

net/sched/act_bpf.c

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act,
7272
case TC_ACT_PIPE:
7373
case TC_ACT_RECLASSIFY:
7474
case TC_ACT_OK:
75+
case TC_ACT_REDIRECT:
7576
action = filter_res;
7677
break;
7778
case TC_ACT_SHOT:

net/sched/cls_bpf.c

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ static int cls_bpf_exec_opcode(int code)
7070
case TC_ACT_PIPE:
7171
case TC_ACT_STOLEN:
7272
case TC_ACT_QUEUED:
73+
case TC_ACT_REDIRECT:
7374
case TC_ACT_UNSPEC:
7475
return code;
7576
default:

samples/bpf/bpf_helpers.h

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ static int (*bpf_get_current_comm)(void *buf, int buf_size) =
3333
(void *) BPF_FUNC_get_current_comm;
3434
static int (*bpf_perf_event_read)(void *map, int index) =
3535
(void *) BPF_FUNC_perf_event_read;
36+
static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) =
37+
(void *) BPF_FUNC_clone_redirect;
38+
static int (*bpf_redirect)(int ifindex, int flags) =
39+
(void *) BPF_FUNC_redirect;
3640

3741
/* llvm builtin functions that eBPF C program may use to
3842
* emit BPF_LD_ABS and BPF_LD_IND instructions

samples/bpf/tcbpf1_kern.c

+23-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <uapi/linux/in.h>
66
#include <uapi/linux/tcp.h>
77
#include <uapi/linux/filter.h>
8-
8+
#include <uapi/linux/pkt_cls.h>
99
#include "bpf_helpers.h"
1010

1111
/* compiler workaround */
@@ -64,4 +64,26 @@ int bpf_prog1(struct __sk_buff *skb)
6464

6565
return 0;
6666
}
67+
SEC("redirect_xmit")
68+
int _redirect_xmit(struct __sk_buff *skb)
69+
{
70+
return bpf_redirect(skb->ifindex + 1, 0);
71+
}
72+
SEC("redirect_recv")
73+
int _redirect_recv(struct __sk_buff *skb)
74+
{
75+
return bpf_redirect(skb->ifindex + 1, 1);
76+
}
77+
SEC("clone_redirect_xmit")
78+
int _clone_redirect_xmit(struct __sk_buff *skb)
79+
{
80+
bpf_clone_redirect(skb, skb->ifindex + 1, 0);
81+
return TC_ACT_SHOT;
82+
}
83+
SEC("clone_redirect_recv")
84+
int _clone_redirect_recv(struct __sk_buff *skb)
85+
{
86+
bpf_clone_redirect(skb, skb->ifindex + 1, 1);
87+
return TC_ACT_SHOT;
88+
}
6789
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)