Skip to content

Commit fbee97f

Browse files
dsahernAlexei Starovoitov
authored andcommitted
bpf: Add support to attach bpf program to a devmap entry
Add BPF_XDP_DEVMAP attach type for use with programs associated with a DEVMAP entry. Allow DEVMAPs to associate a program with a device entry by adding a bpf_prog.fd to 'struct bpf_devmap_val'. Values read show the program id, so the fd and id are a union. bpf programs can get access to the struct via vmlinux.h. The program associated with the fd must have type XDP with expected attach type BPF_XDP_DEVMAP. When a program is associated with a device index, the program is run on an XDP_REDIRECT and before the buffer is added to the per-cpu queue. At this point rxq data is still valid; the next patch adds tx device information allowing the prorgam to see both ingress and egress device indices. XDP generic is skb based and XDP programs do not work with skb's. Block the use case by walking maps used by a program that is to be attached via xdpgeneric and fail if any of them are DEVMAP / DEVMAP_HASH with Block attach of BPF_XDP_DEVMAP programs to devices. Signed-off-by: David Ahern <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Toke Høiland-Jørgensen <[email protected]> Link: https://lore.kernel.org/bpf/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 7f1c042 commit fbee97f

File tree

5 files changed

+109
-4
lines changed

5 files changed

+109
-4
lines changed

include/linux/bpf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,6 +1250,7 @@ int dev_map_enqueue(struct bpf_dtab_netdev *dst, struct xdp_buff *xdp,
12501250
struct net_device *dev_rx);
12511251
int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb,
12521252
struct bpf_prog *xdp_prog);
1253+
bool dev_map_can_have_prog(struct bpf_map *map);
12531254

12541255
struct bpf_cpu_map_entry *__cpu_map_lookup_elem(struct bpf_map *map, u32 key);
12551256
void __cpu_map_flush(void);
@@ -1363,6 +1364,10 @@ static inline struct net_device *__dev_map_hash_lookup_elem(struct bpf_map *map
13631364
{
13641365
return NULL;
13651366
}
1367+
static inline bool dev_map_can_have_prog(struct bpf_map *map)
1368+
{
1369+
return false;
1370+
}
13661371

13671372
static inline void __dev_flush(void)
13681373
{

include/uapi/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ enum bpf_attach_type {
225225
BPF_CGROUP_INET6_GETPEERNAME,
226226
BPF_CGROUP_INET4_GETSOCKNAME,
227227
BPF_CGROUP_INET6_GETSOCKNAME,
228+
BPF_XDP_DEVMAP,
228229
__MAX_BPF_ATTACH_TYPE
229230
};
230231

kernel/bpf/devmap.c

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,17 @@ struct xdp_dev_bulk_queue {
6363
/* DEVMAP values */
6464
struct bpf_devmap_val {
6565
u32 ifindex; /* device index */
66+
union {
67+
int fd; /* prog fd on map write */
68+
u32 id; /* prog id on map read */
69+
} bpf_prog;
6670
};
6771

6872
struct bpf_dtab_netdev {
6973
struct net_device *dev; /* must be first member, due to tracepoint */
7074
struct hlist_node index_hlist;
7175
struct bpf_dtab *dtab;
76+
struct bpf_prog *xdp_prog;
7277
struct rcu_head rcu;
7378
unsigned int idx;
7479
struct bpf_devmap_val val;
@@ -111,12 +116,18 @@ static inline struct hlist_head *dev_map_index_hash(struct bpf_dtab *dtab,
111116

112117
static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr)
113118
{
119+
u32 valsize = attr->value_size;
114120
u64 cost = 0;
115121
int err;
116122

117-
/* check sanity of attributes */
123+
/* check sanity of attributes. 2 value sizes supported:
124+
* 4 bytes: ifindex
125+
* 8 bytes: ifindex + prog fd
126+
*/
118127
if (attr->max_entries == 0 || attr->key_size != 4 ||
119-
attr->value_size != 4 || attr->map_flags & ~DEV_CREATE_FLAG_MASK)
128+
(valsize != offsetofend(struct bpf_devmap_val, ifindex) &&
129+
valsize != offsetofend(struct bpf_devmap_val, bpf_prog.fd)) ||
130+
attr->map_flags & ~DEV_CREATE_FLAG_MASK)
120131
return -EINVAL;
121132

122133
/* Lookup returns a pointer straight to dev->ifindex, so make sure the
@@ -223,6 +234,8 @@ static void dev_map_free(struct bpf_map *map)
223234

224235
hlist_for_each_entry_safe(dev, next, head, index_hlist) {
225236
hlist_del_rcu(&dev->index_hlist);
237+
if (dev->xdp_prog)
238+
bpf_prog_put(dev->xdp_prog);
226239
dev_put(dev->dev);
227240
kfree(dev);
228241
}
@@ -237,6 +250,8 @@ static void dev_map_free(struct bpf_map *map)
237250
if (!dev)
238251
continue;
239252

253+
if (dev->xdp_prog)
254+
bpf_prog_put(dev->xdp_prog);
240255
dev_put(dev->dev);
241256
kfree(dev);
242257
}
@@ -323,6 +338,16 @@ static int dev_map_hash_get_next_key(struct bpf_map *map, void *key,
323338
return -ENOENT;
324339
}
325340

341+
bool dev_map_can_have_prog(struct bpf_map *map)
342+
{
343+
if ((map->map_type == BPF_MAP_TYPE_DEVMAP ||
344+
map->map_type == BPF_MAP_TYPE_DEVMAP_HASH) &&
345+
map->value_size != offsetofend(struct bpf_devmap_val, ifindex))
346+
return true;
347+
348+
return false;
349+
}
350+
326351
static int bq_xmit_all(struct xdp_dev_bulk_queue *bq, u32 flags)
327352
{
328353
struct net_device *dev = bq->dev;
@@ -447,6 +472,30 @@ static inline int __xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp,
447472
return bq_enqueue(dev, xdpf, dev_rx);
448473
}
449474

475+
static struct xdp_buff *dev_map_run_prog(struct net_device *dev,
476+
struct xdp_buff *xdp,
477+
struct bpf_prog *xdp_prog)
478+
{
479+
u32 act;
480+
481+
act = bpf_prog_run_xdp(xdp_prog, xdp);
482+
switch (act) {
483+
case XDP_PASS:
484+
return xdp;
485+
case XDP_DROP:
486+
break;
487+
default:
488+
bpf_warn_invalid_xdp_action(act);
489+
fallthrough;
490+
case XDP_ABORTED:
491+
trace_xdp_exception(dev, xdp_prog, act);
492+
break;
493+
}
494+
495+
xdp_return_buff(xdp);
496+
return NULL;
497+
}
498+
450499
int dev_xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp,
451500
struct net_device *dev_rx)
452501
{
@@ -458,6 +507,11 @@ int dev_map_enqueue(struct bpf_dtab_netdev *dst, struct xdp_buff *xdp,
458507
{
459508
struct net_device *dev = dst->dev;
460509

510+
if (dst->xdp_prog) {
511+
xdp = dev_map_run_prog(dev, xdp, dst->xdp_prog);
512+
if (!xdp)
513+
return 0;
514+
}
461515
return __xdp_enqueue(dev, xdp, dev_rx);
462516
}
463517

@@ -494,6 +548,8 @@ static void __dev_map_entry_free(struct rcu_head *rcu)
494548
struct bpf_dtab_netdev *dev;
495549

496550
dev = container_of(rcu, struct bpf_dtab_netdev, rcu);
551+
if (dev->xdp_prog)
552+
bpf_prog_put(dev->xdp_prog);
497553
dev_put(dev->dev);
498554
kfree(dev);
499555
}
@@ -547,6 +603,7 @@ static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net,
547603
struct bpf_devmap_val *val,
548604
unsigned int idx)
549605
{
606+
struct bpf_prog *prog = NULL;
550607
struct bpf_dtab_netdev *dev;
551608

552609
dev = kmalloc_node(sizeof(*dev), GFP_ATOMIC | __GFP_NOWARN,
@@ -558,11 +615,31 @@ static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net,
558615
if (!dev->dev)
559616
goto err_out;
560617

618+
if (val->bpf_prog.fd >= 0) {
619+
prog = bpf_prog_get_type_dev(val->bpf_prog.fd,
620+
BPF_PROG_TYPE_XDP, false);
621+
if (IS_ERR(prog))
622+
goto err_put_dev;
623+
if (prog->expected_attach_type != BPF_XDP_DEVMAP)
624+
goto err_put_prog;
625+
}
626+
561627
dev->idx = idx;
562628
dev->dtab = dtab;
629+
if (prog) {
630+
dev->xdp_prog = prog;
631+
dev->val.bpf_prog.id = prog->aux->id;
632+
} else {
633+
dev->xdp_prog = NULL;
634+
dev->val.bpf_prog.id = 0;
635+
}
563636
dev->val.ifindex = val->ifindex;
564637

565638
return dev;
639+
err_put_prog:
640+
bpf_prog_put(prog);
641+
err_put_dev:
642+
dev_put(dev->dev);
566643
err_out:
567644
kfree(dev);
568645
return ERR_PTR(-EINVAL);
@@ -572,8 +649,8 @@ static int __dev_map_update_elem(struct net *net, struct bpf_map *map,
572649
void *key, void *value, u64 map_flags)
573650
{
574651
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
652+
struct bpf_devmap_val val = { .bpf_prog.fd = -1 };
575653
struct bpf_dtab_netdev *dev, *old_dev;
576-
struct bpf_devmap_val val = { };
577654
u32 i = *(u32 *)key;
578655

579656
if (unlikely(map_flags > BPF_EXIST))
@@ -588,6 +665,9 @@ static int __dev_map_update_elem(struct net *net, struct bpf_map *map,
588665

589666
if (!val.ifindex) {
590667
dev = NULL;
668+
/* can not specify fd if ifindex is 0 */
669+
if (val.bpf_prog.fd != -1)
670+
return -EINVAL;
591671
} else {
592672
dev = __dev_map_alloc_node(net, dtab, &val, i);
593673
if (IS_ERR(dev))
@@ -616,8 +696,8 @@ static int __dev_map_hash_update_elem(struct net *net, struct bpf_map *map,
616696
void *key, void *value, u64 map_flags)
617697
{
618698
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
699+
struct bpf_devmap_val val = { .bpf_prog.fd = -1 };
619700
struct bpf_dtab_netdev *dev, *old_dev;
620-
struct bpf_devmap_val val = { };
621701
u32 idx = *(u32 *)key;
622702
unsigned long flags;
623703
int err = -EEXIST;

net/core/dev.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5420,6 +5420,18 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp)
54205420
struct bpf_prog *new = xdp->prog;
54215421
int ret = 0;
54225422

5423+
if (new) {
5424+
u32 i;
5425+
5426+
/* generic XDP does not work with DEVMAPs that can
5427+
* have a bpf_prog installed on an entry
5428+
*/
5429+
for (i = 0; i < new->aux->used_map_cnt; i++) {
5430+
if (dev_map_can_have_prog(new->aux->used_maps[i]))
5431+
return -EINVAL;
5432+
}
5433+
}
5434+
54235435
switch (xdp->command) {
54245436
case XDP_SETUP_PROG:
54255437
rcu_assign_pointer(dev->xdp_prog, new);
@@ -8835,6 +8847,12 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack,
88358847
return -EINVAL;
88368848
}
88378849

8850+
if (prog->expected_attach_type == BPF_XDP_DEVMAP) {
8851+
NL_SET_ERR_MSG(extack, "BPF_XDP_DEVMAP programs can not be attached to a device");
8852+
bpf_prog_put(prog);
8853+
return -EINVAL;
8854+
}
8855+
88388856
/* prog->aux->id may be 0 for orphaned device-bound progs */
88398857
if (prog->aux->id && prog->aux->id == prog_id) {
88408858
bpf_prog_put(prog);

tools/include/uapi/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ enum bpf_attach_type {
225225
BPF_CGROUP_INET6_GETPEERNAME,
226226
BPF_CGROUP_INET4_GETSOCKNAME,
227227
BPF_CGROUP_INET6_GETSOCKNAME,
228+
BPF_XDP_DEVMAP,
228229
__MAX_BPF_ATTACH_TYPE
229230
};
230231

0 commit comments

Comments
 (0)