diff options
| author | Toke Høiland-Jørgensen <toke@redhat.com> | 2021-11-04 21:32:48 +0100 |
|---|---|---|
| committer | Toke Høiland-Jørgensen <toke@redhat.com> | 2021-11-09 21:35:12 +0100 |
| commit | de44bbfd9cf981baab181c006dc363c5f412d94c (patch) | |
| tree | b609c9dfdf919950944774e450b3062ba907c91d | |
| parent | 135ff3e170ca9b50a99ac6b25e742c7f93f2a92d (diff) | |
| download | linux-de44bbfd9cf981baab181c006dc363c5f412d94c.tar.gz | |
bpf: Add helpers to dequeue from a PIFO map
This adds a new helper to dequeue a packet from a PIFO map,
bpf_packet_dequeue(). The helper returns a refcounted pointer to the packet
dequeued from the map; the reference must be released either by dropping
the packet, or by returning it to the caller. This is achieved through
bpf_packet_drop() and bpf_packet_return() respectively.
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
| -rw-r--r-- | include/uapi/linux/bpf.h | 28 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 17 | ||||
| -rw-r--r-- | net/core/filter.c | 57 | ||||
| -rw-r--r-- | tools/include/uapi/linux/bpf.h | 28 |
4 files changed, 125 insertions, 5 deletions
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index bf1bc7644f0a1..909a8b74c2cfe 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -4959,6 +4959,31 @@ union bpf_attr { * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. * **-EBUSY** if failed to try lock mmap_lock. * **-EINVAL** for invalid **flags**. + * + * long bpf_packet_dequeue(void *ctx, struct bpf_map *map, u64 flags) + * Description + * Dequeue the packet at the head of the PIFO in *map* and return a pointer + * to the packet (or NULL if the PIFO is empty). + * Return + * On success, a pointer to the packet, or NULL if the PIFO is empty. The + * packet pointer must be freed using *bpf_packet_drop()* or *bpf_packet_return()* + * + * long bpf_packet_drop(void *ctx, void *pkt) + * Description + * Drop *pkt*, which must be a reference previously returned by + * *bpf_packet_dequeue()* (and checked to not be NULL). + * Return + * This always succeeds and returns zero. + * + * long bpf_packet_return(void *ctx, void *pkt) + * Description + * Return *pkt* to the kernel for further processing. The *pkt* pointer must + * be a reference previously returned by *bpf_packet_dequeue()* (and checked + * to not be NULL). If this is called multiple times with different packets, + * subsequent calls will cause earlier packets to be dropped (only one packet + * can be returned). + * Return + * This always succeeds and returns zero. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5142,6 +5167,9 @@ union bpf_attr { FN(skc_to_unix_sock), \ FN(kallsyms_lookup_name), \ FN(find_vma), \ + FN(packet_dequeue), \ + FN(packet_drop), \ + FN(packet_return), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7e30ecd0713ed..c176fcf7700af 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -500,7 +500,9 @@ static bool is_release_function(enum bpf_func_id func_id) { return func_id == BPF_FUNC_sk_release || func_id == BPF_FUNC_ringbuf_submit || - func_id == BPF_FUNC_ringbuf_discard; + func_id == BPF_FUNC_ringbuf_discard || + func_id == BPF_FUNC_packet_drop || + func_id == BPF_FUNC_packet_return; } static bool may_be_acquire_function(enum bpf_func_id func_id) @@ -509,7 +511,8 @@ static bool may_be_acquire_function(enum bpf_func_id func_id) func_id == BPF_FUNC_sk_lookup_udp || func_id == BPF_FUNC_skc_lookup_tcp || func_id == BPF_FUNC_map_lookup_elem || - func_id == BPF_FUNC_ringbuf_reserve; + func_id == BPF_FUNC_ringbuf_reserve || + func_id == BPF_FUNC_packet_dequeue; } static bool is_acquire_function(enum bpf_func_id func_id, @@ -520,7 +523,8 @@ static bool is_acquire_function(enum bpf_func_id func_id, if (func_id == BPF_FUNC_sk_lookup_tcp || func_id == BPF_FUNC_sk_lookup_udp || func_id == BPF_FUNC_skc_lookup_tcp || - func_id == BPF_FUNC_ringbuf_reserve) + func_id == BPF_FUNC_ringbuf_reserve || + func_id == BPF_FUNC_packet_dequeue) return true; if (func_id == BPF_FUNC_map_lookup_elem && @@ -5602,7 +5606,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, goto error; break; case BPF_MAP_TYPE_PIFO: - if (func_id != BPF_FUNC_redirect_map) + if (func_id != BPF_FUNC_redirect_map && + func_id != BPF_FUNC_packet_dequeue) goto error; break; default: @@ -5700,6 +5705,10 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, if (map->map_type != BPF_MAP_TYPE_TASK_STORAGE) goto error; break; + case BPF_FUNC_packet_dequeue: + if (map->map_type != BPF_MAP_TYPE_PIFO) + goto error; + break; default: break; } diff --git a/net/core/filter.c b/net/core/filter.c index 849ac9d593278..3643338427210 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4147,6 +4147,52 @@ static const struct bpf_func_proto bpf_xdp_redirect_map_proto = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_packet_dequeue, struct dequeue_data *, ctx, struct bpf_map *, map, + u64, flags) +{ + return (unsigned long)pifo_map_dequeue(map, flags); +} + +static const struct bpf_func_proto bpf_packet_dequeue_proto = { + .func = bpf_packet_dequeue, + .gpl_only = false, + .ret_type = RET_PTR_TO_QUEUED_PKT_OR_NULL, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, +}; + +BPF_CALL_2(bpf_packet_drop, struct dequeue_data *, ctx, struct xdp_frame *, pkt) +{ + xdp_return_frame(pkt); + return 0; +} + +static const struct bpf_func_proto bpf_packet_drop_proto = { + .func = bpf_packet_drop, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_QUEUED_PKT, +}; + +BPF_CALL_2(bpf_packet_return, struct dequeue_data *, ctx, struct xdp_frame *, pkt) +{ + if (ctx->dequeued_pkt) + xdp_return_frame(ctx->dequeued_pkt); + ctx->dequeued_pkt = pkt; + return 0; +} + +static const struct bpf_func_proto bpf_packet_return_proto = { + .func = bpf_packet_return, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_QUEUED_PKT, +}; + + static unsigned long bpf_skb_copy(void *dst_buff, const void *skb, unsigned long off, unsigned long len) { @@ -7504,7 +7550,16 @@ xdp_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) static const struct bpf_func_proto * dequeue_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { - return bpf_base_func_proto(func_id); + switch (func_id) { + case BPF_FUNC_packet_dequeue: + return &bpf_packet_dequeue_proto; + case BPF_FUNC_packet_return: + return &bpf_packet_return_proto; + case BPF_FUNC_packet_drop: + return &bpf_packet_drop_proto; + default: + return bpf_base_func_proto(func_id); + } } const struct bpf_func_proto bpf_sock_map_update_proto __weak; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index bf1bc7644f0a1..909a8b74c2cfe 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -4959,6 +4959,31 @@ union bpf_attr { * **-ENOENT** if *task->mm* is NULL, or no vma contains *addr*. * **-EBUSY** if failed to try lock mmap_lock. * **-EINVAL** for invalid **flags**. + * + * long bpf_packet_dequeue(void *ctx, struct bpf_map *map, u64 flags) + * Description + * Dequeue the packet at the head of the PIFO in *map* and return a pointer + * to the packet (or NULL if the PIFO is empty). + * Return + * On success, a pointer to the packet, or NULL if the PIFO is empty. The + * packet pointer must be freed using *bpf_packet_drop()* or *bpf_packet_return()* + * + * long bpf_packet_drop(void *ctx, void *pkt) + * Description + * Drop *pkt*, which must be a reference previously returned by + * *bpf_packet_dequeue()* (and checked to not be NULL). + * Return + * This always succeeds and returns zero. + * + * long bpf_packet_return(void *ctx, void *pkt) + * Description + * Return *pkt* to the kernel for further processing. The *pkt* pointer must + * be a reference previously returned by *bpf_packet_dequeue()* (and checked + * to not be NULL). If this is called multiple times with different packets, + * subsequent calls will cause earlier packets to be dropped (only one packet + * can be returned). + * Return + * This always succeeds and returns zero. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -5142,6 +5167,9 @@ union bpf_attr { FN(skc_to_unix_sock), \ FN(kallsyms_lookup_name), \ FN(find_vma), \ + FN(packet_dequeue), \ + FN(packet_drop), \ + FN(packet_return), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper |
