Skip to content

Commit 9aa1206

Browse files
borkmannAlexei Starovoitov
authored and
Alexei Starovoitov
committed
bpf: Add redirect_peer helper
Add an efficient ingress to ingress netns switch that can be used out of tc BPF programs in order to redirect traffic from host ns ingress into a container veth device ingress without having to go via CPU backlog queue [0]. For local containers this can also be utilized and path via CPU backlog queue only needs to be taken once, not twice. On a high level this borrows from ipvlan which does similar switch in __netif_receive_skb_core() and then iterates via another_round. This helps to reduce latency for mentioned use cases. Pod to remote pod with redirect(), TCP_RR [1]: # percpu_netperf 10.217.1.33 RT_LATENCY: 122.450 (per CPU: 122.666 122.401 122.333 122.401 ) MEAN_LATENCY: 121.210 (per CPU: 121.100 121.260 121.320 121.160 ) STDDEV_LATENCY: 120.040 (per CPU: 119.420 119.910 125.460 115.370 ) MIN_LATENCY: 46.500 (per CPU: 47.000 47.000 47.000 45.000 ) P50_LATENCY: 118.500 (per CPU: 118.000 119.000 118.000 119.000 ) P90_LATENCY: 127.500 (per CPU: 127.000 128.000 127.000 128.000 ) P99_LATENCY: 130.750 (per CPU: 131.000 131.000 129.000 132.000 ) TRANSACTION_RATE: 32666.400 (per CPU: 8152.200 8169.842 8174.439 8169.897 ) Pod to remote pod with redirect_peer(), TCP_RR: # percpu_netperf 10.217.1.33 RT_LATENCY: 44.449 (per CPU: 43.767 43.127 45.279 45.622 ) MEAN_LATENCY: 45.065 (per CPU: 44.030 45.530 45.190 45.510 ) STDDEV_LATENCY: 84.823 (per CPU: 66.770 97.290 84.380 90.850 ) MIN_LATENCY: 33.500 (per CPU: 33.000 33.000 34.000 34.000 ) P50_LATENCY: 43.250 (per CPU: 43.000 43.000 43.000 44.000 ) P90_LATENCY: 46.750 (per CPU: 46.000 47.000 47.000 47.000 ) P99_LATENCY: 52.750 (per CPU: 51.000 54.000 53.000 53.000 ) TRANSACTION_RATE: 90039.500 (per CPU: 22848.186 23187.089 22085.077 21919.130 ) [0] https://linuxplumbersconf.org/event/7/contributions/674/attachments/568/1002/plumbers_2020_cilium_load_balancer.pdf [1] https://github.com/borkmann/netperf_scripts/blob/master/percpu_netperf Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent dd2ce6a commit 9aa1206

File tree

6 files changed

+106
-10
lines changed

6 files changed

+106
-10
lines changed

drivers/net/veth.c

+9
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,14 @@ static int veth_select_rxq(struct net_device *dev)
420420
return smp_processor_id() % dev->real_num_rx_queues;
421421
}
422422

423+
static struct net_device *veth_peer_dev(struct net_device *dev)
424+
{
425+
struct veth_priv *priv = netdev_priv(dev);
426+
427+
/* Callers must be under RCU read side. */
428+
return rcu_dereference(priv->peer);
429+
}
430+
423431
static int veth_xdp_xmit(struct net_device *dev, int n,
424432
struct xdp_frame **frames,
425433
u32 flags, bool ndo_xmit)
@@ -1224,6 +1232,7 @@ static const struct net_device_ops veth_netdev_ops = {
12241232
.ndo_set_rx_headroom = veth_set_rx_headroom,
12251233
.ndo_bpf = veth_xdp,
12261234
.ndo_xdp_xmit = veth_ndo_xdp_xmit,
1235+
.ndo_get_peer_dev = veth_peer_dev,
12271236
};
12281237

12291238
#define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HW_CSUM | \

include/linux/netdevice.h

+4
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,9 @@ struct netdev_net_notifier {
12771277
* int (*ndo_tunnel_ctl)(struct net_device *dev, struct ip_tunnel_parm *p,
12781278
* int cmd);
12791279
* Add, change, delete or get information on an IPv4 tunnel.
1280+
* struct net_device *(*ndo_get_peer_dev)(struct net_device *dev);
1281+
* If a device is paired with a peer device, return the peer instance.
1282+
* The caller must be under RCU read context.
12801283
*/
12811284
struct net_device_ops {
12821285
int (*ndo_init)(struct net_device *dev);
@@ -1484,6 +1487,7 @@ struct net_device_ops {
14841487
struct devlink_port * (*ndo_get_devlink_port)(struct net_device *dev);
14851488
int (*ndo_tunnel_ctl)(struct net_device *dev,
14861489
struct ip_tunnel_parm *p, int cmd);
1490+
struct net_device * (*ndo_get_peer_dev)(struct net_device *dev);
14871491
};
14881492

14891493
/**

include/uapi/linux/bpf.h

+17
Original file line numberDiff line numberDiff line change
@@ -3719,6 +3719,22 @@ union bpf_attr {
37193719
* never return NULL.
37203720
* Return
37213721
* A pointer pointing to the kernel percpu variable on this cpu.
3722+
*
3723+
* long bpf_redirect_peer(u32 ifindex, u64 flags)
3724+
* Description
3725+
* Redirect the packet to another net device of index *ifindex*.
3726+
* This helper is somewhat similar to **bpf_redirect**\ (), except
3727+
* that the redirection happens to the *ifindex*' peer device and
3728+
* the netns switch takes place from ingress to ingress without
3729+
* going through the CPU's backlog queue.
3730+
*
3731+
* The *flags* argument is reserved and must be 0. The helper is
3732+
* currently only supported for tc BPF program types at the ingress
3733+
* hook and for veth device types. The peer device must reside in a
3734+
* different network namespace.
3735+
* Return
3736+
* The helper returns **TC_ACT_REDIRECT** on success or
3737+
* **TC_ACT_SHOT** on error.
37223738
*/
37233739
#define __BPF_FUNC_MAPPER(FN) \
37243740
FN(unspec), \
@@ -3876,6 +3892,7 @@ union bpf_attr {
38763892
FN(redirect_neigh), \
38773893
FN(bpf_per_cpu_ptr), \
38783894
FN(bpf_this_cpu_ptr), \
3895+
FN(redirect_peer), \
38793896
/* */
38803897

38813898
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

net/core/dev.c

+12-3
Original file line numberDiff line numberDiff line change
@@ -4930,7 +4930,7 @@ EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook);
49304930

49314931
static inline struct sk_buff *
49324932
sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret,
4933-
struct net_device *orig_dev)
4933+
struct net_device *orig_dev, bool *another)
49344934
{
49354935
#ifdef CONFIG_NET_CLS_ACT
49364936
struct mini_Qdisc *miniq = rcu_dereference_bh(skb->dev->miniq_ingress);
@@ -4974,7 +4974,11 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret,
49744974
* redirecting to another netdev
49754975
*/
49764976
__skb_push(skb, skb->mac_len);
4977-
skb_do_redirect(skb);
4977+
if (skb_do_redirect(skb) == -EAGAIN) {
4978+
__skb_pull(skb, skb->mac_len);
4979+
*another = true;
4980+
break;
4981+
}
49784982
return NULL;
49794983
case TC_ACT_CONSUMED:
49804984
return NULL;
@@ -5163,7 +5167,12 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
51635167
skip_taps:
51645168
#ifdef CONFIG_NET_INGRESS
51655169
if (static_branch_unlikely(&ingress_needed_key)) {
5166-
skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev);
5170+
bool another = false;
5171+
5172+
skb = sch_handle_ingress(skb, &pt_prev, &ret, orig_dev,
5173+
&another);
5174+
if (another)
5175+
goto another_round;
51675176
if (!skb)
51685177
goto out;
51695178

net/core/filter.c

+47-7
Original file line numberDiff line numberDiff line change
@@ -2380,8 +2380,9 @@ static int __bpf_redirect_neigh(struct sk_buff *skb, struct net_device *dev)
23802380

23812381
/* Internal, non-exposed redirect flags. */
23822382
enum {
2383-
BPF_F_NEIGH = (1ULL << 1),
2384-
#define BPF_F_REDIRECT_INTERNAL (BPF_F_NEIGH)
2383+
BPF_F_NEIGH = (1ULL << 1),
2384+
BPF_F_PEER = (1ULL << 2),
2385+
#define BPF_F_REDIRECT_INTERNAL (BPF_F_NEIGH | BPF_F_PEER)
23852386
};
23862387

23872388
BPF_CALL_3(bpf_clone_redirect, struct sk_buff *, skb, u32, ifindex, u64, flags)
@@ -2430,19 +2431,35 @@ EXPORT_PER_CPU_SYMBOL_GPL(bpf_redirect_info);
24302431
int skb_do_redirect(struct sk_buff *skb)
24312432
{
24322433
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
2434+
struct net *net = dev_net(skb->dev);
24332435
struct net_device *dev;
24342436
u32 flags = ri->flags;
24352437

2436-
dev = dev_get_by_index_rcu(dev_net(skb->dev), ri->tgt_index);
2438+
dev = dev_get_by_index_rcu(net, ri->tgt_index);
24372439
ri->tgt_index = 0;
2438-
if (unlikely(!dev)) {
2439-
kfree_skb(skb);
2440-
return -EINVAL;
2440+
ri->flags = 0;
2441+
if (unlikely(!dev))
2442+
goto out_drop;
2443+
if (flags & BPF_F_PEER) {
2444+
const struct net_device_ops *ops = dev->netdev_ops;
2445+
2446+
if (unlikely(!ops->ndo_get_peer_dev ||
2447+
!skb_at_tc_ingress(skb)))
2448+
goto out_drop;
2449+
dev = ops->ndo_get_peer_dev(dev);
2450+
if (unlikely(!dev ||
2451+
!is_skb_forwardable(dev, skb) ||
2452+
net_eq(net, dev_net(dev))))
2453+
goto out_drop;
2454+
skb->dev = dev;
2455+
return -EAGAIN;
24412456
}
2442-
24432457
return flags & BPF_F_NEIGH ?
24442458
__bpf_redirect_neigh(skb, dev) :
24452459
__bpf_redirect(skb, dev, flags);
2460+
out_drop:
2461+
kfree_skb(skb);
2462+
return -EINVAL;
24462463
}
24472464

24482465
BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags)
@@ -2466,6 +2483,27 @@ static const struct bpf_func_proto bpf_redirect_proto = {
24662483
.arg2_type = ARG_ANYTHING,
24672484
};
24682485

2486+
BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
2487+
{
2488+
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
2489+
2490+
if (unlikely(flags))
2491+
return TC_ACT_SHOT;
2492+
2493+
ri->flags = BPF_F_PEER;
2494+
ri->tgt_index = ifindex;
2495+
2496+
return TC_ACT_REDIRECT;
2497+
}
2498+
2499+
static const struct bpf_func_proto bpf_redirect_peer_proto = {
2500+
.func = bpf_redirect_peer,
2501+
.gpl_only = false,
2502+
.ret_type = RET_INTEGER,
2503+
.arg1_type = ARG_ANYTHING,
2504+
.arg2_type = ARG_ANYTHING,
2505+
};
2506+
24692507
BPF_CALL_2(bpf_redirect_neigh, u32, ifindex, u64, flags)
24702508
{
24712509
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
@@ -7053,6 +7091,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
70537091
return &bpf_redirect_proto;
70547092
case BPF_FUNC_redirect_neigh:
70557093
return &bpf_redirect_neigh_proto;
7094+
case BPF_FUNC_redirect_peer:
7095+
return &bpf_redirect_peer_proto;
70567096
case BPF_FUNC_get_route_realm:
70577097
return &bpf_get_route_realm_proto;
70587098
case BPF_FUNC_get_hash_recalc:

tools/include/uapi/linux/bpf.h

+17
Original file line numberDiff line numberDiff line change
@@ -3719,6 +3719,22 @@ union bpf_attr {
37193719
* never return NULL.
37203720
* Return
37213721
* A pointer pointing to the kernel percpu variable on this cpu.
3722+
*
3723+
* long bpf_redirect_peer(u32 ifindex, u64 flags)
3724+
* Description
3725+
* Redirect the packet to another net device of index *ifindex*.
3726+
* This helper is somewhat similar to **bpf_redirect**\ (), except
3727+
* that the redirection happens to the *ifindex*' peer device and
3728+
* the netns switch takes place from ingress to ingress without
3729+
* going through the CPU's backlog queue.
3730+
*
3731+
* The *flags* argument is reserved and must be 0. The helper is
3732+
* currently only supported for tc BPF program types at the ingress
3733+
* hook and for veth device types. The peer device must reside in a
3734+
* different network namespace.
3735+
* Return
3736+
* The helper returns **TC_ACT_REDIRECT** on success or
3737+
* **TC_ACT_SHOT** on error.
37223738
*/
37233739
#define __BPF_FUNC_MAPPER(FN) \
37243740
FN(unspec), \
@@ -3876,6 +3892,7 @@ union bpf_attr {
38763892
FN(redirect_neigh), \
38773893
FN(bpf_per_cpu_ptr), \
38783894
FN(bpf_this_cpu_ptr), \
3895+
FN(redirect_peer), \
38793896
/* */
38803897

38813898
/* integer value in 'imm' field of BPF_CALL instruction selects which helper

0 commit comments

Comments
 (0)