get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/98802/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 98802,
    "url": "http://patches.dpdk.org/api/patches/98802/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20210913181510.46058-5-stephen@networkplumber.org/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20210913181510.46058-5-stephen@networkplumber.org>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20210913181510.46058-5-stephen@networkplumber.org",
    "date": "2021-09-13T18:15:02",
    "name": "[v8,04/12] bpf: add function to convert classic BPF to DPDK BPF",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "f4ddd2ed44bbeac55989866a2f31ecafd4b37e5d",
    "submitter": {
        "id": 27,
        "url": "http://patches.dpdk.org/api/people/27/?format=api",
        "name": "Stephen Hemminger",
        "email": "stephen@networkplumber.org"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20210913181510.46058-5-stephen@networkplumber.org/mbox/",
    "series": [
        {
            "id": 18882,
            "url": "http://patches.dpdk.org/api/series/18882/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=18882",
            "date": "2021-09-13T18:14:58",
            "name": "Packet capture framework enhancements",
            "version": 8,
            "mbox": "http://patches.dpdk.org/series/18882/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/98802/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/98802/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "X-Original-To": "patchwork@inbox.dpdk.org",
        "Delivered-To": "patchwork@inbox.dpdk.org",
        "Received": [
            "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id B2543A0C45;\n\tMon, 13 Sep 2021 20:15:37 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id BA30E410F6;\n\tMon, 13 Sep 2021 20:15:24 +0200 (CEST)",
            "from mail-pf1-f174.google.com (mail-pf1-f174.google.com\n [209.85.210.174])\n by mails.dpdk.org (Postfix) with ESMTP id 0AFF0410EF\n for <dev@dpdk.org>; Mon, 13 Sep 2021 20:15:19 +0200 (CEST)",
            "by mail-pf1-f174.google.com with SMTP id x7so9620929pfa.8\n for <dev@dpdk.org>; Mon, 13 Sep 2021 11:15:18 -0700 (PDT)",
            "from hermes.local (204-195-33-123.wavecable.com. [204.195.33.123])\n by smtp.gmail.com with ESMTPSA id b10sm7616888pfl.220.2021.09.13.11.15.16\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 13 Sep 2021 11:15:16 -0700 (PDT)"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=networkplumber-org.20150623.gappssmtp.com; s=20150623;\n h=from:to:cc:subject:date:message-id:in-reply-to:references\n :mime-version:content-transfer-encoding;\n bh=+1F5ZFyy11bPkCLy9DZj7R5ScXtDss+tPfONt1qvxb4=;\n b=GrAB/SG8iYQ6vg7uC8WlPWtpyJZcZlJ/LaVUfK9SPCQ/Mjte1vjLUK6yK98a6XBpWF\n KAkLYNpzUHWTsCFFDJ8ump5scGaQmLDyOi5mffPufnOSEkkNgOyxIq2MFBFFGTA7DJSK\n dgre4ouxdpCu69EGM2eIq/atbiPt1UEFmEJQdMv5xmAi5QxpohC+1bEXWrIBo7rioaoN\n TOxJopt9Dq1Vsojafe1RxKA33k5CT0fyCRSv6GuH6XVghUzqxOx81gKNubPK+8JbHZBz\n UC0SKU19VBLW0dxZRx5DsKK9xlNjdCcSeNH5z1aO4RKJA4QZqCqiH5YfjnqG+IwRKvDN\n 8jIw==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20210112;\n h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n :references:mime-version:content-transfer-encoding;\n bh=+1F5ZFyy11bPkCLy9DZj7R5ScXtDss+tPfONt1qvxb4=;\n b=pkukxbmi0bNWCbdvUpptG9oQeNQpjB3N1NBvCz0yxswRAVu1uhkP0knkhhwZ/C9ycj\n EFv6TKRdvpyeLB/yvB/k+6RQjqjWvYGjveqfEikvJXHXVa3f15CMU16/W3+/ZGc78wzG\n 3vS9waYIK0qoIucvkTvjOrfO56w2HTYxl5/3bcl7y5GVXJ7szvu9TpKHu6TauLdIsTis\n /wOO6XLJStfk8KA9xo5RIh8EojDMCBqfZYoXYc43cGwirpevasKKnSfIObWyXt1cx4ch\n j1VbHJZ1DX2xoHNXjh1vAurVoppUmWfmT/gnhqEDv0VP3zPEs3fmOgA7wUjeVon7izkf\n C4ZA==",
        "X-Gm-Message-State": "AOAM53381635+iUYdNnj7/gZXfYnBsz5Vdh7W3Y3j1Q96aICdS/CF1UO\n TQ8OwWmESOPvYL+71REGLbi342XsWvCo4g==",
        "X-Google-Smtp-Source": "\n ABdhPJxhgPa97x+xUvEqphizgm5IVjXcfUomOG2tFXsTCLkvhWWeKlOLGsRwjVxXHFG1HA36e3UN5Q==",
        "X-Received": "by 2002:a65:6213:: with SMTP id\n d19mr12276250pgv.110.1631556917277;\n Mon, 13 Sep 2021 11:15:17 -0700 (PDT)",
        "From": "Stephen Hemminger <stephen@networkplumber.org>",
        "To": "dev@dpdk.org",
        "Cc": "Stephen Hemminger <stephen@networkplumber.org>",
        "Date": "Mon, 13 Sep 2021 11:15:02 -0700",
        "Message-Id": "<20210913181510.46058-5-stephen@networkplumber.org>",
        "X-Mailer": "git-send-email 2.30.2",
        "In-Reply-To": "<20210913181510.46058-1-stephen@networkplumber.org>",
        "References": "<20210903004732.109023-1-stephen@networkplumber.org>\n <20210913181510.46058-1-stephen@networkplumber.org>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dpdk-dev] [PATCH v8 04/12] bpf: add function to convert classic\n BPF to DPDK BPF",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "The pcap library emits classic BPF (32 bit) and is useful for\ncreating filter programs.  The DPDK BPF library only implements\nextended BPF (eBPF).  Add an function to convert from old to\nnew.\n\nThe rte_bpf_convert function uses rte_malloc to put the resulting\nprogram in hugepage shared memory so it can be passed from a\nsecondary process to a primary process.\n\nThe code to convert was originally done as part of the Linux\nkernel implementation then converted to a userspace program.\nBoth authors have agreed that it is allowable to license this\nas BSD licensed in DPDK.\n\nSigned-off-by: Stephen Hemminger <stephen@networkplumber.org>\n---\n lib/bpf/bpf_convert.c | 570 ++++++++++++++++++++++++++++++++++++++++++\n lib/bpf/meson.build   |   5 +\n lib/bpf/rte_bpf.h     |  25 ++\n lib/bpf/version.map   |   6 +\n 4 files changed, 606 insertions(+)\n create mode 100644 lib/bpf/bpf_convert.c",
    "diff": "diff --git a/lib/bpf/bpf_convert.c b/lib/bpf/bpf_convert.c\nnew file mode 100644\nindex 000000000000..a46ffeb067dd\n--- /dev/null\n+++ b/lib/bpf/bpf_convert.c\n@@ -0,0 +1,570 @@\n+/* SPDX-License-Identifier: BSD-3-Clause\n+ * Copyright(c) 2021 Microsoft Corporation\n+ *\n+ * Based on bpf_convert_filter() in the Linux kernel sources\n+ * and filter2xdp.\n+ *\n+ * Licensed as BSD with permission original authors.\n+ * Copyright (C) 2017 Tobias Klauser\n+ * Copyright (c) 2011 - 2014 PLUMgrid, http://plumgrid.com\n+ */\n+\n+#include <assert.h>\n+#include <errno.h>\n+#include <stdbool.h>\n+#include <stddef.h>\n+#include <stdint.h>\n+#include <stdlib.h>\n+#include <string.h>\n+\n+#include <rte_common.h>\n+#include <rte_bpf.h>\n+#include <rte_log.h>\n+#include <rte_malloc.h>\n+#include <rte_errno.h>\n+\n+/* Workaround name conflicts with libpcap */\n+#define bpf_validate(f, len) bpf_validate_libpcap(f, len)\n+#include <pcap/pcap.h>\n+#include <pcap/bpf.h>\n+#undef bpf_validate\n+\n+#include \"bpf_impl.h\"\n+#include \"bpf_def.h\"\n+\n+#ifndef BPF_MAXINSNS\n+#define BPF_MAXINSNS 4096\n+#endif\n+\n+/*\n+ * Linux socket filter uses negative absolute offsets to\n+ * reference ancillary data.\n+ */\n+#define SKF_AD_OFF    (-0x1000)\n+#define SKF_AD_PROTOCOL 0\n+#define SKF_AD_PKTTYPE\t4\n+#define SKF_AD_IFINDEX\t8\n+#define SKF_AD_NLATTR\t12\n+#define SKF_AD_NLATTR_NEST\t16\n+#define SKF_AD_MARK\t20\n+#define SKF_AD_QUEUE\t24\n+#define SKF_AD_HATYPE\t28\n+#define SKF_AD_RXHASH\t32\n+#define SKF_AD_CPU\t36\n+#define SKF_AD_ALU_XOR_X\t40\n+#define SKF_AD_VLAN_TAG\t44\n+#define SKF_AD_VLAN_TAG_PRESENT 48\n+#define SKF_AD_PAY_OFFSET\t52\n+#define SKF_AD_RANDOM\t56\n+#define SKF_AD_VLAN_TPID\t60\n+#define SKF_AD_MAX\t64\n+\n+/* ArgX, context and stack frame pointer register positions. Note,\n+ * Arg1, Arg2, Arg3, etc are used as argument mappings of function\n+ * calls in BPF_CALL instruction.\n+ */\n+#define BPF_REG_ARG1\tEBPF_REG_1\n+#define BPF_REG_ARG2\tEBPF_REG_2\n+#define BPF_REG_ARG3\tEBPF_REG_3\n+#define BPF_REG_ARG4\tEBPF_REG_4\n+#define BPF_REG_ARG5\tEBPF_REG_5\n+#define BPF_REG_CTX\tEBPF_REG_6\n+#define BPF_REG_FP\tEBPF_REG_10\n+\n+/* Additional register mappings for converted user programs. */\n+#define BPF_REG_A\tEBPF_REG_0\n+#define BPF_REG_X\tEBPF_REG_7\n+#define BPF_REG_TMP\tEBPF_REG_8\n+\n+/* Helper macros for filter block array initializers. */\n+\n+/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */\n+\n+#define EBPF_ALU64_REG(OP, DST, SRC)\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = EBPF_ALU64 | BPF_OP(OP) | BPF_X,\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = 0 })\n+\n+#define BPF_ALU32_REG(OP, DST, SRC)\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_ALU | BPF_OP(OP) | BPF_X,\t\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = 0 })\n+\n+/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */\n+\n+#define BPF_ALU32_IMM(OP, DST, IMM)\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_ALU | BPF_OP(OP) | BPF_K,\t\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = 0,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = IMM })\n+\n+/* Short form of mov, dst_reg = src_reg */\n+\n+#define BPF_MOV64_REG(DST, SRC)\t\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = EBPF_ALU64 | EBPF_MOV | BPF_X,\t\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = 0 })\n+\n+#define BPF_MOV32_REG(DST, SRC)\t\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_ALU | EBPF_MOV | BPF_X,\t\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = 0 })\n+\n+/* Short form of mov, dst_reg = imm32 */\n+\n+#define BPF_MOV32_IMM(DST, IMM)\t\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_ALU | EBPF_MOV | BPF_K,\t\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = 0,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = IMM })\n+\n+/* Short form of mov based on type, BPF_X: dst_reg = src_reg, BPF_K: dst_reg = imm32 */\n+\n+#define BPF_MOV32_RAW(TYPE, DST, SRC, IMM)\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_ALU | EBPF_MOV | BPF_SRC(TYPE),\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = IMM })\n+\n+/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */\n+\n+#define BPF_LD_ABS(SIZE, IMM)\t\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS,\t\\\n+\t\t.dst_reg = 0,\t\t\t\t\t\\\n+\t\t.src_reg = 0,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = IMM })\n+\n+/* Memory load, dst_reg = *(uint *) (src_reg + off16) */\n+\n+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = OFF,\t\t\t\t\t\\\n+\t\t.imm   = 0 })\n+\n+/* Memory store, *(uint *) (dst_reg + off16) = src_reg */\n+\n+#define BPF_STX_MEM(SIZE, DST, SRC, OFF)\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = OFF,\t\t\t\t\t\\\n+\t\t.imm   = 0 })\n+\n+/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */\n+\n+#define BPF_JMP_IMM(OP, DST, IMM, OFF)\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_JMP | BPF_OP(OP) | BPF_K,\t\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = 0,\t\t\t\t\t\\\n+\t\t.off   = OFF,\t\t\t\t\t\\\n+\t\t.imm   = IMM })\n+\n+/* Raw code statement block */\n+\n+#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = CODE,\t\t\t\t\t\\\n+\t\t.dst_reg = DST,\t\t\t\t\t\\\n+\t\t.src_reg = SRC,\t\t\t\t\t\\\n+\t\t.off   = OFF,\t\t\t\t\t\\\n+\t\t.imm   = IMM })\n+\n+/* Program exit */\n+\n+#define BPF_EXIT_INSN()\t\t\t\t\t\t\\\n+\t((struct ebpf_insn) {\t\t\t\t\t\\\n+\t\t.code  = BPF_JMP | EBPF_EXIT,\t\t\t\\\n+\t\t.dst_reg = 0,\t\t\t\t\t\\\n+\t\t.src_reg = 0,\t\t\t\t\t\\\n+\t\t.off   = 0,\t\t\t\t\t\\\n+\t\t.imm   = 0 })\n+\n+/*\n+ * Placeholder to convert BPF extensions like length and VLAN tag\n+ * If and when DPDK BPF supports them.\n+ */\n+static bool convert_bpf_load(const struct bpf_insn *fp,\n+\t\t\t     struct ebpf_insn **new_insnp __rte_unused)\n+{\n+\tswitch (fp->k) {\n+\tcase SKF_AD_OFF + SKF_AD_PROTOCOL:\n+\tcase SKF_AD_OFF + SKF_AD_PKTTYPE:\n+\tcase SKF_AD_OFF + SKF_AD_IFINDEX:\n+\tcase SKF_AD_OFF + SKF_AD_HATYPE:\n+\tcase SKF_AD_OFF + SKF_AD_MARK:\n+\tcase SKF_AD_OFF + SKF_AD_RXHASH:\n+\tcase SKF_AD_OFF + SKF_AD_QUEUE:\n+\tcase SKF_AD_OFF + SKF_AD_VLAN_TAG:\n+\tcase SKF_AD_OFF + SKF_AD_VLAN_TAG_PRESENT:\n+\tcase SKF_AD_OFF + SKF_AD_VLAN_TPID:\n+\tcase SKF_AD_OFF + SKF_AD_PAY_OFFSET:\n+\tcase SKF_AD_OFF + SKF_AD_NLATTR:\n+\tcase SKF_AD_OFF + SKF_AD_NLATTR_NEST:\n+\tcase SKF_AD_OFF + SKF_AD_CPU:\n+\tcase SKF_AD_OFF + SKF_AD_RANDOM:\n+\tcase SKF_AD_OFF + SKF_AD_ALU_XOR_X:\n+\t\t/* Linux has special negative offsets to access meta-data. */\n+\t\tRTE_BPF_LOG(ERR,\n+\t\t\t    \"rte_bpf_convert: socket offset %d not supported\\n\",\n+\t\t\t    fp->k - SKF_AD_OFF);\n+\t\treturn true;\n+\tdefault:\n+\t\treturn false;\n+\t}\n+}\n+\n+static int bpf_convert_filter(const struct bpf_insn *prog, size_t len,\n+\t\t\t      struct ebpf_insn *new_prog, uint32_t *new_len)\n+{\n+\tunsigned int pass = 0;\n+\tsize_t new_flen = 0, target, i;\n+\tstruct ebpf_insn *new_insn;\n+\tconst struct bpf_insn *fp;\n+\tint *addrs = NULL;\n+\tuint8_t bpf_src;\n+\n+\tif (len > BPF_MAXINSNS) {\n+\t\tRTE_BPF_LOG(ERR, \"%s: cBPF program too long (%zu insns)\\n\",\n+\t\t\t    __func__, len);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* On second pass, allocate the new program */\n+\tif (new_prog) {\n+\t\taddrs = calloc(len, sizeof(*addrs));\n+\t\tif (addrs == NULL)\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+do_pass:\n+\tnew_insn = new_prog;\n+\tfp = prog;\n+\n+\t/* Classic BPF related prologue emission. */\n+\tif (new_insn) {\n+\t\t/* Classic BPF expects A and X to be reset first. These need\n+\t\t * to be guaranteed to be the first two instructions.\n+\t\t */\n+\t\t*new_insn++ = EBPF_ALU64_REG(BPF_XOR, BPF_REG_A, BPF_REG_A);\n+\t\t*new_insn++ = EBPF_ALU64_REG(BPF_XOR, BPF_REG_X, BPF_REG_X);\n+\n+\t\t/* All programs must keep CTX in callee saved BPF_REG_CTX.\n+\t\t * In eBPF case it's done by the compiler, here we need to\n+\t\t * do this ourself. Initial CTX is present in BPF_REG_ARG1.\n+\t\t */\n+\t\t*new_insn++ = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);\n+\t} else {\n+\t\tnew_insn += 3;\n+\t}\n+\n+\tfor (i = 0; i < len; fp++, i++) {\n+\t\tstruct ebpf_insn tmp_insns[6] = { };\n+\t\tstruct ebpf_insn *insn = tmp_insns;\n+\n+\t\tif (addrs)\n+\t\t\taddrs[i] = new_insn - new_prog;\n+\n+\t\tswitch (fp->code) {\n+\t\t\t/* Absolute loads are how classic BPF accesses skb */\n+\t\tcase BPF_LD | BPF_ABS | BPF_W:\n+\t\tcase BPF_LD | BPF_ABS | BPF_H:\n+\t\tcase BPF_LD | BPF_ABS | BPF_B:\n+\t\t\tif (convert_bpf_load(fp, &insn))\n+\t\t\t\tgoto err;\n+\n+\t\t\t*insn = BPF_RAW_INSN(fp->code, 0, 0, 0, fp->k);\n+\t\t\tbreak;\n+\n+\t\tcase BPF_ALU | BPF_DIV | BPF_X:\n+\t\tcase BPF_ALU | BPF_MOD | BPF_X:\n+\t\t\t/* For cBPF, don't cause floating point exception */\n+\t\t\t*insn++ = BPF_MOV32_REG(BPF_REG_X, BPF_REG_X);\n+\t\t\t*insn++ = BPF_JMP_IMM(EBPF_JNE, BPF_REG_X, 0, 2);\n+\t\t\t*insn++ = BPF_ALU32_REG(BPF_XOR, BPF_REG_A, BPF_REG_A);\n+\t\t\t*insn++ = BPF_EXIT_INSN();\n+\t\t\t/* fallthrough */\n+\t\tcase BPF_ALU | BPF_ADD | BPF_X:\n+\t\tcase BPF_ALU | BPF_ADD | BPF_K:\n+\t\tcase BPF_ALU | BPF_SUB | BPF_X:\n+\t\tcase BPF_ALU | BPF_SUB | BPF_K:\n+\t\tcase BPF_ALU | BPF_AND | BPF_X:\n+\t\tcase BPF_ALU | BPF_AND | BPF_K:\n+\t\tcase BPF_ALU | BPF_OR | BPF_X:\n+\t\tcase BPF_ALU | BPF_OR | BPF_K:\n+\t\tcase BPF_ALU | BPF_LSH | BPF_X:\n+\t\tcase BPF_ALU | BPF_LSH | BPF_K:\n+\t\tcase BPF_ALU | BPF_RSH | BPF_X:\n+\t\tcase BPF_ALU | BPF_RSH | BPF_K:\n+\t\tcase BPF_ALU | BPF_XOR | BPF_X:\n+\t\tcase BPF_ALU | BPF_XOR | BPF_K:\n+\t\tcase BPF_ALU | BPF_MUL | BPF_X:\n+\t\tcase BPF_ALU | BPF_MUL | BPF_K:\n+\t\tcase BPF_ALU | BPF_DIV | BPF_K:\n+\t\tcase BPF_ALU | BPF_MOD | BPF_K:\n+\t\tcase BPF_ALU | BPF_NEG:\n+\t\tcase BPF_LD | BPF_IND | BPF_W:\n+\t\tcase BPF_LD | BPF_IND | BPF_H:\n+\t\tcase BPF_LD | BPF_IND | BPF_B:\n+\t\t\t/* All arithmetic insns map as-is. */\n+\t\t\t*insn = BPF_RAW_INSN(fp->code, BPF_REG_A, BPF_REG_X, 0, fp->k);\n+\t\t\tbreak;\n+\n+\t\t\t/* Jump transformation cannot use BPF block macros\n+\t\t\t * everywhere as offset calculation and target updates\n+\t\t\t * require a bit more work than the rest, i.e. jump\n+\t\t\t * opcodes map as-is, but offsets need adjustment.\n+\t\t\t */\n+\n+#define BPF_EMIT_JMP\t\t\t\t\t\t\t\\\n+\t\t\tdo {\t\t\t\t\t\t\\\n+\t\t\t\tif (target >= len)\t\t\t\\\n+\t\t\t\t\tgoto err;\t\t\t\\\n+\t\t\t\tinsn->off = addrs ? addrs[target] - addrs[i] - 1 : 0; \\\n+\t\t\t\t/* Adjust pc relative offset for 2nd or 3rd insn. */ \\\n+\t\t\t\tinsn->off -= insn - tmp_insns;\t\t\\\n+\t\t\t} while (0)\n+\n+\t\tcase BPF_JMP | BPF_JA:\n+\t\t\ttarget = i + fp->k + 1;\n+\t\t\tinsn->code = fp->code;\n+\t\t\tBPF_EMIT_JMP;\n+\t\t\tbreak;\n+\n+\t\tcase BPF_JMP | BPF_JEQ | BPF_K:\n+\t\tcase BPF_JMP | BPF_JEQ | BPF_X:\n+\t\tcase BPF_JMP | BPF_JSET | BPF_K:\n+\t\tcase BPF_JMP | BPF_JSET | BPF_X:\n+\t\tcase BPF_JMP | BPF_JGT | BPF_K:\n+\t\tcase BPF_JMP | BPF_JGT | BPF_X:\n+\t\tcase BPF_JMP | BPF_JGE | BPF_K:\n+\t\tcase BPF_JMP | BPF_JGE | BPF_X:\n+\t\t\tif (BPF_SRC(fp->code) == BPF_K && (int) fp->k < 0) {\n+\t\t\t\t/* BPF immediates are signed, zero extend\n+\t\t\t\t * immediate into tmp register and use it\n+\t\t\t\t * in compare insn.\n+\t\t\t\t */\n+\t\t\t\t*insn++ = BPF_MOV32_IMM(BPF_REG_TMP, fp->k);\n+\n+\t\t\t\tinsn->dst_reg = BPF_REG_A;\n+\t\t\t\tinsn->src_reg = BPF_REG_TMP;\n+\t\t\t\tbpf_src = BPF_X;\n+\t\t\t} else {\n+\t\t\t\tinsn->dst_reg = BPF_REG_A;\n+\t\t\t\tinsn->imm = fp->k;\n+\t\t\t\tbpf_src = BPF_SRC(fp->code);\n+\t\t\t\tinsn->src_reg = bpf_src == BPF_X ? BPF_REG_X : 0;\n+\t\t\t}\n+\n+\t\t\t/* Common case where 'jump_false' is next insn. */\n+\t\t\tif (fp->jf == 0) {\n+\t\t\t\tinsn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;\n+\t\t\t\ttarget = i + fp->jt + 1;\n+\t\t\t\tBPF_EMIT_JMP;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\t/* Convert JEQ into JNE when 'jump_true' is next insn. */\n+\t\t\tif (fp->jt == 0 && BPF_OP(fp->code) == BPF_JEQ) {\n+\t\t\t\tinsn->code = BPF_JMP | EBPF_JNE | bpf_src;\n+\t\t\t\ttarget = i + fp->jf + 1;\n+\t\t\t\tBPF_EMIT_JMP;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\t/* Other jumps are mapped into two insns: Jxx and JA. */\n+\t\t\ttarget = i + fp->jt + 1;\n+\t\t\tinsn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src;\n+\t\t\tBPF_EMIT_JMP;\n+\t\t\tinsn++;\n+\n+\t\t\tinsn->code = BPF_JMP | BPF_JA;\n+\t\t\ttarget = i + fp->jf + 1;\n+\t\t\tBPF_EMIT_JMP;\n+\t\t\tbreak;\n+\n+\t\t\t/* ldxb 4 * ([14] & 0xf) is remaped into 6 insns. */\n+\t\tcase BPF_LDX | BPF_MSH | BPF_B:\n+\t\t\t/* tmp = A */\n+\t\t\t*insn++ = BPF_MOV64_REG(BPF_REG_TMP, BPF_REG_A);\n+\t\t\t/* A = BPF_R0 = *(u8 *) (skb->data + K) */\n+\t\t\t*insn++ = BPF_LD_ABS(BPF_B, fp->k);\n+\t\t\t/* A &= 0xf */\n+\t\t\t*insn++ = BPF_ALU32_IMM(BPF_AND, BPF_REG_A, 0xf);\n+\t\t\t/* A <<= 2 */\n+\t\t\t*insn++ = BPF_ALU32_IMM(BPF_LSH, BPF_REG_A, 2);\n+\t\t\t/* X = A */\n+\t\t\t*insn++ = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A);\n+\t\t\t/* A = tmp */\n+\t\t\t*insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_TMP);\n+\t\t\tbreak;\n+\n+\t\t\t/* RET_K is remaped into 2 insns. RET_A case doesn't need an\n+\t\t\t * extra mov as EBPF_REG_0 is already mapped into BPF_REG_A.\n+\t\t\t */\n+\t\tcase BPF_RET | BPF_A:\n+\t\tcase BPF_RET | BPF_K:\n+\t\t\tif (BPF_RVAL(fp->code) == BPF_K) {\n+\t\t\t\t*insn++ = BPF_MOV32_RAW(BPF_K, EBPF_REG_0,\n+\t\t\t\t\t\t\t0, fp->k);\n+\t\t\t}\n+\t\t\t*insn = BPF_EXIT_INSN();\n+\t\t\tbreak;\n+\n+\t\t\t/* Store to stack. */\n+\t\tcase BPF_ST:\n+\t\tcase BPF_STX:\n+\t\t\t*insn = BPF_STX_MEM(BPF_W, BPF_REG_FP, BPF_CLASS(fp->code) ==\n+\t\t\t\t\t    BPF_ST ? BPF_REG_A : BPF_REG_X,\n+\t\t\t\t\t    -(BPF_MEMWORDS - fp->k) * 4);\n+\t\t\tbreak;\n+\n+\t\t\t/* Load from stack. */\n+\t\tcase BPF_LD | BPF_MEM:\n+\t\tcase BPF_LDX | BPF_MEM:\n+\t\t\t*insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD  ?\n+\t\t\t\t\t    BPF_REG_A : BPF_REG_X, BPF_REG_FP,\n+\t\t\t\t\t    -(BPF_MEMWORDS - fp->k) * 4);\n+\t\t\tbreak;\n+\n+\t\t\t/* A = K or X = K */\n+\t\tcase BPF_LD | BPF_IMM:\n+\t\tcase BPF_LDX | BPF_IMM:\n+\t\t\t*insn = BPF_MOV32_IMM(BPF_CLASS(fp->code) == BPF_LD ?\n+\t\t\t\t\t      BPF_REG_A : BPF_REG_X, fp->k);\n+\t\t\tbreak;\n+\n+\t\t\t/* X = A */\n+\t\tcase BPF_MISC | BPF_TAX:\n+\t\t\t*insn = BPF_MOV64_REG(BPF_REG_X, BPF_REG_A);\n+\t\t\tbreak;\n+\n+\t\t\t/* A = X */\n+\t\tcase BPF_MISC | BPF_TXA:\n+\t\t\t*insn = BPF_MOV64_REG(BPF_REG_A, BPF_REG_X);\n+\t\t\tbreak;\n+\n+\t\t\t/* A = mbuf->len or X = mbuf->len */\n+\t\tcase BPF_LD | BPF_W | BPF_LEN:\n+\t\tcase BPF_LDX | BPF_W | BPF_LEN:\n+\t\t\t/* BPF_ABS/BPF_IND implicitly expect mbuf ptr in R6 */\n+\n+\t\t\t*insn = BPF_LDX_MEM(BPF_W, BPF_CLASS(fp->code) == BPF_LD ?\n+\t\t\t\t\t    BPF_REG_A : BPF_REG_X, BPF_REG_CTX,\n+\t\t\t\t\t    offsetof(struct rte_mbuf, pkt_len));\n+\t\t\tbreak;\n+\n+\t\t\t/* Unknown instruction. */\n+\t\tdefault:\n+\t\t\tRTE_BPF_LOG(ERR, \"%s: Unknown instruction!: %#x\\n\",\n+\t\t\t\t    __func__, fp->code);\n+\t\t\tgoto err;\n+\t\t}\n+\n+\t\tinsn++;\n+\t\tif (new_prog)\n+\t\t\tmemcpy(new_insn, tmp_insns,\n+\t\t\t       sizeof(*insn) * (insn - tmp_insns));\n+\t\tnew_insn += insn - tmp_insns;\n+\t}\n+\n+\tif (!new_prog) {\n+\t\t/* Only calculating new length. */\n+\t\t*new_len = new_insn - new_prog;\n+\t\treturn 0;\n+\t}\n+\n+\tpass++;\n+\tif ((ptrdiff_t)new_flen != new_insn - new_prog) {\n+\t\tnew_flen = new_insn - new_prog;\n+\t\tif (pass > 2)\n+\t\t\tgoto err;\n+\t\tgoto do_pass;\n+\t}\n+\n+\tfree(addrs);\n+\tassert(*new_len == new_flen);\n+\n+\treturn 0;\n+err:\n+\tfree(addrs);\n+\treturn -1;\n+}\n+\n+struct rte_bpf_prm *\n+rte_bpf_convert(const struct bpf_program *prog)\n+{\n+\tstruct rte_bpf_prm *prm = NULL;\n+\tstruct ebpf_insn *ebpf = NULL;\n+\tuint32_t ebpf_len = 0;\n+\tint ret;\n+\n+\tif (prog == NULL) {\n+\t\tRTE_BPF_LOG(ERR, \"%s: NULL program\\n\", __func__);\n+\t\trte_errno = EINVAL;\n+\t\treturn NULL;\n+\t}\n+\n+\t/* 1st pass: calculate the eBPF program length */\n+\tret = bpf_convert_filter(prog->bf_insns, prog->bf_len, NULL, &ebpf_len);\n+\tif (ret < 0) {\n+\t\tRTE_BPF_LOG(ERR, \"%s: cannot get eBPF length\\n\", __func__);\n+\t\trte_errno = -ret;\n+\t\treturn NULL;\n+\t}\n+\n+\tRTE_BPF_LOG(DEBUG, \"%s: prog len cBPF=%u -> eBPF=%u\\n\",\n+\t\t    __func__, prog->bf_len, ebpf_len);\n+\n+\tprm = rte_zmalloc(\"bpf_filter\",\n+\t\t\t  sizeof(*prm) + ebpf_len * sizeof(*ebpf), 0);\n+\tif (prm == NULL) {\n+\t\trte_errno = ENOMEM;\n+\t\treturn NULL;\n+\t}\n+\n+\t/* The EPBF instructions in this case are right after the header */\n+\tebpf = (void *)(prm + 1);\n+\n+\t/* 2nd pass: remap cBPF to eBPF instructions  */\n+\tret = bpf_convert_filter(prog->bf_insns, prog->bf_len, ebpf, &ebpf_len);\n+\tif (ret < 0) {\n+\t\tRTE_BPF_LOG(ERR, \"%s: cannot convert cBPF to eBPF\\n\", __func__);\n+\t\tfree(prm);\n+\t\trte_errno = -ret;\n+\t\treturn NULL;\n+\t}\n+\n+\tprm->ins = ebpf;\n+\tprm->nb_ins = ebpf_len;\n+\n+\t/* Classic BPF programs use mbufs */\n+\tprm->prog_arg.type = RTE_BPF_ARG_PTR_MBUF;\n+\tprm->prog_arg.size = sizeof(struct rte_mbuf);\n+\n+\treturn prm;\n+}\ndiff --git a/lib/bpf/meson.build b/lib/bpf/meson.build\nindex 63cbd60185e0..54f7610ae990 100644\n--- a/lib/bpf/meson.build\n+++ b/lib/bpf/meson.build\n@@ -25,3 +25,8 @@ if dep.found()\n     sources += files('bpf_load_elf.c')\n     ext_deps += dep\n endif\n+\n+if dpdk_conf.has('RTE_PORT_PCAP')\n+    sources += files('bpf_convert.c')\n+    ext_deps += pcap_dep\n+endif\ndiff --git a/lib/bpf/rte_bpf.h b/lib/bpf/rte_bpf.h\nindex 69116f36ba8b..2f23e272a376 100644\n--- a/lib/bpf/rte_bpf.h\n+++ b/lib/bpf/rte_bpf.h\n@@ -198,6 +198,31 @@ rte_bpf_exec_burst(const struct rte_bpf *bpf, void *ctx[], uint64_t rc[],\n int\n rte_bpf_get_jit(const struct rte_bpf *bpf, struct rte_bpf_jit *jit);\n \n+#ifdef RTE_PORT_PCAP\n+\n+struct bpf_program;\n+\n+/**\n+ * Convert a Classic BPF program from libpcap into a DPDK BPF code.\n+ *\n+ * @param prog\n+ *  Classic BPF program from pcap_compile().\n+ * @param prm\n+ *  Result Extended BPF program.\n+ * @return\n+ *   Pointer to BPF program (allocated with *rte_malloc*)\n+ *   that is used in future BPF operations,\n+ *   or NULL on error, with error code set in rte_errno.\n+ *   Possible rte_errno errors include:\n+ *   - EINVAL - invalid parameter passed to function\n+ *   - ENOMEM - can't reserve enough memory\n+ */\n+__rte_experimental\n+struct rte_bpf_prm *\n+rte_bpf_convert(const struct bpf_program *prog);\n+\n+#endif\n+\n #ifdef __cplusplus\n }\n #endif\ndiff --git a/lib/bpf/version.map b/lib/bpf/version.map\nindex 0bf35f487666..47082d5003ef 100644\n--- a/lib/bpf/version.map\n+++ b/lib/bpf/version.map\n@@ -14,3 +14,9 @@ DPDK_22 {\n \n \tlocal: *;\n };\n+\n+EXPERIMENTAL {\n+\tglobal:\n+\n+\trte_bpf_convert;\n+};\n",
    "prefixes": [
        "v8",
        "04/12"
    ]
}