aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorToke Høiland-Jørgensen <toke@redhat.com>2021-11-04 21:32:48 +0100
committerToke Høiland-Jørgensen <toke@redhat.com>2021-11-09 21:35:12 +0100
commitde44bbfd9cf981baab181c006dc363c5f412d94c (patch)
treeb609c9dfdf919950944774e450b3062ba907c91d
parent135ff3e170ca9b50a99ac6b25e742c7f93f2a92d (diff)
downloadlinux-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.h28
-rw-r--r--kernel/bpf/verifier.c17
-rw-r--r--net/core/filter.c57
-rw-r--r--tools/include/uapi/linux/bpf.h28
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