Skip to content

Commit 43e74c0

Browse files
tohojoborkmann
authored andcommitted
bpf_xdp_redirect_map: Perform map lookup in eBPF helper
The bpf_redirect_map() helper used by XDP programs doesn't return any indication of whether it can successfully redirect to the map index it was given. Instead, BPF programs have to track this themselves, leading to programs using duplicate maps to track which entries are populated in the devmap. This patch fixes this by moving the map lookup into the bpf_redirect_map() helper, which makes it possible to return failure to the eBPF program. The lower bits of the flags argument is used as the return code, which means that existing users who pass a '0' flag argument will get XDP_ABORTED. With this, a BPF program can check the return code from the helper call and react by, for instance, substituting a different redirect. This works for any type of map used for redirect. Signed-off-by: Toke Høiland-Jørgensen <[email protected]> Acked-by: Jonathan Lemon <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent 4b55cf2 commit 43e74c0

File tree

4 files changed

+26
-19
lines changed

4 files changed

+26
-19
lines changed

include/linux/filter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@ struct bpf_skb_data_end {
580580
struct bpf_redirect_info {
581581
u32 flags;
582582
u32 tgt_index;
583+
void *tgt_value;
583584
struct bpf_map *map;
584585
struct bpf_map *map_to_flush;
585586
u32 kern_flags;

include/trace/events/xdp.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,8 @@ struct _bpf_dtab_netdev {
175175
#endif /* __DEVMAP_OBJ_TYPE */
176176

177177
#define devmap_ifindex(fwd, map) \
178-
(!fwd ? 0 : \
179-
((map->map_type == BPF_MAP_TYPE_DEVMAP) ? \
180-
((struct _bpf_dtab_netdev *)fwd)->dev->ifindex : 0))
178+
((map->map_type == BPF_MAP_TYPE_DEVMAP) ? \
179+
((struct _bpf_dtab_netdev *)fwd)->dev->ifindex : 0)
181180

182181
#define _trace_xdp_redirect_map(dev, xdp, fwd, map, idx) \
183182
trace_xdp_redirect_map(dev, xdp, devmap_ifindex(fwd, map), \

include/uapi/linux/bpf.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,8 +1571,11 @@ union bpf_attr {
15711571
* but this is only implemented for native XDP (with driver
15721572
* support) as of this writing).
15731573
*
1574-
* All values for *flags* are reserved for future usage, and must
1575-
* be left at zero.
1574+
* The lower two bits of *flags* are used as the return code if
1575+
* the map lookup fails. This is so that the return value can be
1576+
* one of the XDP program return codes up to XDP_TX, as chosen by
1577+
* the caller. Any higher bits in the *flags* argument must be
1578+
* unset.
15761579
*
15771580
* When used to redirect packets to net devices, this helper
15781581
* provides a high performance increase over **bpf_redirect**\ ().

net/core/filter.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3605,17 +3605,13 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp,
36053605
struct bpf_redirect_info *ri)
36063606
{
36073607
u32 index = ri->tgt_index;
3608-
void *fwd = NULL;
3608+
void *fwd = ri->tgt_value;
36093609
int err;
36103610

36113611
ri->tgt_index = 0;
3612+
ri->tgt_value = NULL;
36123613
WRITE_ONCE(ri->map, NULL);
36133614

3614-
fwd = __xdp_map_lookup_elem(map, index);
3615-
if (unlikely(!fwd)) {
3616-
err = -EINVAL;
3617-
goto err;
3618-
}
36193615
if (ri->map_to_flush && unlikely(ri->map_to_flush != map))
36203616
xdp_do_flush_map();
36213617

@@ -3652,18 +3648,13 @@ static int xdp_do_generic_redirect_map(struct net_device *dev,
36523648
{
36533649
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
36543650
u32 index = ri->tgt_index;
3655-
void *fwd = NULL;
3651+
void *fwd = ri->tgt_value;
36563652
int err = 0;
36573653

36583654
ri->tgt_index = 0;
3655+
ri->tgt_value = NULL;
36593656
WRITE_ONCE(ri->map, NULL);
36603657

3661-
fwd = __xdp_map_lookup_elem(map, index);
3662-
if (unlikely(!fwd)) {
3663-
err = -EINVAL;
3664-
goto err;
3665-
}
3666-
36673658
if (map->map_type == BPF_MAP_TYPE_DEVMAP) {
36683659
struct bpf_dtab_netdev *dst = fwd;
36693660

@@ -3732,6 +3723,7 @@ BPF_CALL_2(bpf_xdp_redirect, u32, ifindex, u64, flags)
37323723

37333724
ri->flags = flags;
37343725
ri->tgt_index = ifindex;
3726+
ri->tgt_value = NULL;
37353727
WRITE_ONCE(ri->map, NULL);
37363728

37373729
return XDP_REDIRECT;
@@ -3750,9 +3742,21 @@ BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u32, ifindex,
37503742
{
37513743
struct bpf_redirect_info *ri = this_cpu_ptr(&bpf_redirect_info);
37523744

3753-
if (unlikely(flags))
3745+
/* Lower bits of the flags are used as return code on lookup failure */
3746+
if (unlikely(flags > XDP_TX))
37543747
return XDP_ABORTED;
37553748

3749+
ri->tgt_value = __xdp_map_lookup_elem(map, ifindex);
3750+
if (unlikely(!ri->tgt_value)) {
3751+
/* If the lookup fails we want to clear out the state in the
3752+
* redirect_info struct completely, so that if an eBPF program
3753+
* performs multiple lookups, the last one always takes
3754+
* precedence.
3755+
*/
3756+
WRITE_ONCE(ri->map, NULL);
3757+
return flags;
3758+
}
3759+
37563760
ri->flags = flags;
37573761
ri->tgt_index = ifindex;
37583762
WRITE_ONCE(ri->map, map);

0 commit comments

Comments
 (0)