@@ -5341,6 +5341,23 @@ union bpf_attr {
* **-EACCES** if the SYN cookie is not valid.
*
* **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * long bpf_packet_dequeue(void *ctx, struct bpf_map *map, u64 flags, u64 *rank)
+ * 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 returning
+ * the packet pointer. The *rank* pointer will be set to the rank of
+ * the dequeued packet on success, or a negative error code on error.
+ *
+ * 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.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -5551,6 +5568,8 @@ union bpf_attr {
FN(tcp_raw_gen_syncookie_ipv6), \
FN(tcp_raw_check_syncookie_ipv4), \
FN(tcp_raw_check_syncookie_ipv6), \
+ FN(packet_dequeue), \
+ FN(packet_drop), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -483,7 +483,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,
@@ -495,7 +496,8 @@ static bool is_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_ringbuf_reserve ||
- func_id == BPF_FUNC_kptr_xchg)
+ func_id == BPF_FUNC_kptr_xchg ||
+ func_id == BPF_FUNC_packet_dequeue)
return true;
if (func_id == BPF_FUNC_map_lookup_elem &&
@@ -6276,7 +6278,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
goto error;
break;
case BPF_MAP_TYPE_PIFO_XDP:
- if (func_id != BPF_FUNC_redirect_map)
+ if (func_id != BPF_FUNC_redirect_map &&
+ func_id != BPF_FUNC_packet_dequeue)
goto error;
break;
default:
@@ -6385,6 +6388,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_XDP)
+ goto error;
+ break;
default:
break;
}
@@ -4430,6 +4430,40 @@ static const struct bpf_func_proto bpf_xdp_redirect_map_proto = {
.arg3_type = ARG_ANYTHING,
};
+BTF_ID_LIST_SINGLE(xdp_md_btf_ids, struct, xdp_md)
+
+BPF_CALL_4(bpf_packet_dequeue, struct dequeue_data *, ctx, struct bpf_map *, map,
+ u64, flags, u64 *, rank)
+{
+ return (unsigned long)pifo_map_dequeue(map, flags, rank);
+}
+
+static const struct bpf_func_proto bpf_packet_dequeue_proto = {
+ .func = bpf_packet_dequeue,
+ .gpl_only = false,
+ .ret_type = RET_PTR_TO_BTF_ID_OR_NULL,
+ .ret_btf_id = xdp_md_btf_ids,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_CONST_MAP_PTR,
+ .arg3_type = ARG_ANYTHING,
+ .arg4_type = ARG_PTR_TO_LONG,
+};
+
+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_BTF_ID | OBJ_RELEASE,
+ .arg2_btf_id = xdp_md_btf_ids,
+};
+
static unsigned long bpf_skb_copy(void *dst_buff, const void *skb,
unsigned long off, unsigned long len)
{
@@ -8065,7 +8099,14 @@ 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_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;
@@ -5341,6 +5341,23 @@ union bpf_attr {
* **-EACCES** if the SYN cookie is not valid.
*
* **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * long bpf_packet_dequeue(void *ctx, struct bpf_map *map, u64 flags, u64 *rank)
+ * 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 returning
+ * the packet pointer. The *rank* pointer will be set to the rank of
+ * the dequeued packet on success, or a negative error code on error.
+ *
+ * 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.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -5551,6 +5568,8 @@ union bpf_attr {
FN(tcp_raw_gen_syncookie_ipv6), \
FN(tcp_raw_check_syncookie_ipv4), \
FN(tcp_raw_check_syncookie_ipv6), \
+ FN(packet_dequeue), \
+ FN(packet_drop), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
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 (using bpf_packet_drop()), or by returning it to the caller. Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com> --- include/uapi/linux/bpf.h | 19 +++++++++++++++ kernel/bpf/verifier.c | 13 +++++++--- net/core/filter.c | 43 +++++++++++++++++++++++++++++++++- tools/include/uapi/linux/bpf.h | 19 +++++++++++++++ 4 files changed, 90 insertions(+), 4 deletions(-)