get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 818457,
    "url": "http://patchwork.ozlabs.org/api/patches/818457/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/1506404199-23579-3-git-send-email-yliu@fridaylinux.org/",
    "project": {
        "id": 47,
        "url": "http://patchwork.ozlabs.org/api/projects/47/?format=api",
        "name": "Open vSwitch",
        "link_name": "openvswitch",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "git@github.com:openvswitch/ovs.git",
        "webscm_url": "https://github.com/openvswitch/ovs",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<1506404199-23579-3-git-send-email-yliu@fridaylinux.org>",
    "list_archive_url": null,
    "date": "2017-09-26T05:36:32",
    "name": "[ovs-dev,v3,2/9] dpif-netdev: retrieve flow directly from the flow mark",
    "commit_ref": null,
    "pull_url": null,
    "state": "changes-requested",
    "archived": false,
    "hash": "b0221ae5975ef64856d12dd6069edeaeb91407a4",
    "submitter": {
        "id": 72215,
        "url": "http://patchwork.ozlabs.org/api/people/72215/?format=api",
        "name": "Yuanhan Liu",
        "email": "yliu@fridaylinux.org"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/1506404199-23579-3-git-send-email-yliu@fridaylinux.org/mbox/",
    "series": [
        {
            "id": 5061,
            "url": "http://patchwork.ozlabs.org/api/series/5061/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/openvswitch/list/?series=5061",
            "date": "2017-09-26T05:36:30",
            "name": "OVS-DPDK flow offload with rte_flow",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/5061/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/818457/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/818457/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@bilbo.ozlabs.org",
            "ovs-dev@mail.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "ozlabs.org;\n\tspf=pass (mailfrom) smtp.mailfrom=openvswitch.org\n\t(client-ip=140.211.169.12; helo=mail.linuxfoundation.org;\n\tenvelope-from=ovs-dev-bounces@openvswitch.org;\n\treceiver=<UNKNOWN>)",
            "ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=fridaylinux-org.20150623.gappssmtp.com\n\theader.i=@fridaylinux-org.20150623.gappssmtp.com\n\theader.b=\"qfjKkgrd\"; dkim-atps=neutral"
        ],
        "Received": [
            "from mail.linuxfoundation.org (mail.linuxfoundation.org\n\t[140.211.169.12])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3y1VB31gCRz9t3R\n\tfor <incoming@patchwork.ozlabs.org>;\n\tTue, 26 Sep 2017 15:38:51 +1000 (AEST)",
            "from mail.linux-foundation.org (localhost [127.0.0.1])\n\tby mail.linuxfoundation.org (Postfix) with ESMTP id 85266BC2;\n\tTue, 26 Sep 2017 05:38:36 +0000 (UTC)",
            "from smtp1.linuxfoundation.org (smtp1.linux-foundation.org\n\t[172.17.192.35])\n\tby mail.linuxfoundation.org (Postfix) with ESMTPS id 6DB54B13\n\tfor <dev@openvswitch.org>; Tue, 26 Sep 2017 05:38:34 +0000 (UTC)",
            "from mail-pg0-f41.google.com (mail-pg0-f41.google.com\n\t[74.125.83.41])\n\tby smtp1.linuxfoundation.org (Postfix) with ESMTPS id ADDBF367\n\tfor <dev@openvswitch.org>; Tue, 26 Sep 2017 05:38:32 +0000 (UTC)",
            "by mail-pg0-f41.google.com with SMTP id i195so5359858pgd.9\n\tfor <dev@openvswitch.org>; Mon, 25 Sep 2017 22:38:32 -0700 (PDT)",
            "from localhost.localdomain ([101.228.205.132])\n\tby smtp.gmail.com with ESMTPSA id\n\to79sm13180077pfi.108.2017.09.25.22.38.09\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128);\n\tMon, 25 Sep 2017 22:38:30 -0700 (PDT)"
        ],
        "X-Greylist": "whitelisted by SQLgrey-1.7.6",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=fridaylinux-org.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=6k6dGeZxKBsUvhexApcbQGpxDWkoa57zrtOQK5LlCj8=;\n\tb=qfjKkgrdpj4JIuIHFxqh+2zZtq5E5OTutgv+dvM972TYWeI32U7y+0HRebf1AiIkKR\n\taWaLSX8fF1IMqQ1FJVgXURlXPlIB+KguByqN9IXFEMcojz6gq6QzQvwKuBSxSUPUXKkc\n\tr6wbfBN7Gq4c9WuQHSg8X7bPSSP+SkHntEnQgTfNMqoqh6ErjTopa1Nvp5oU1bNC93Tg\n\to/lIW7Xa5iqDfvk5hgptVqlH2BxdUSrrrA/H8Mgm2pKBm+3XmMiri33xOHx5Apo3NQ8l\n\tqjk9amPz/SSlfH4odJLhWpfV0FYke1LyhqoAv8j8OQuF/CTR+eAM/P3q8+vNBVm5bdfE\n\tqpTg==",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=6k6dGeZxKBsUvhexApcbQGpxDWkoa57zrtOQK5LlCj8=;\n\tb=tZNMhVA7uXpfqPlpn0c7+Hjqlau/Iz2QGURnxfN4Zvv0SEd7aRzozNrq1usBKQM16g\n\t6zUeiPdmyM/v9jzGohvBl5sKJ3MQZTHbZJbVBks7qETmRYXFIU2I6qql7AtZTnMTiPWC\n\tpYgb+Jdutky8AvGlbvBpe6ZKGXqhqx7m3ghCalGXDd984/XTZdaozZbTmoo6YyVFS3v1\n\tNG8xwbDxNtyVjQwhP1KDGF0IhYs03qUjA2KCZcsPIhRiQjg4jdYLk3oH/ouve76iBO2o\n\t7YukIBp/ua6ktKJibV0pocCFmi6hxYH9PKlYAJyxKe0ps2DXTDg0lTS4psOD8abkNoZc\n\tvZ6A==",
        "X-Gm-Message-State": "AHPjjUi4HMqT6bIrf5oWwUkHLktNHGTHdQijI586uLosSYn6vAz5pWd7\n\tC3JApUA2emaANH1bkPyb49Up3fUjruy/tQ==",
        "X-Google-Smtp-Source": "AOwi7QCQe+BoeqN9Q/sd+gzapGcNdEW4i2Kz0gPq32X8735y1vJeRHSAla0WUVoYjn0A/wzG6z/dGQ==",
        "X-Received": "by 10.98.15.13 with SMTP id x13mr9833289pfi.249.1506404311746;\n\tMon, 25 Sep 2017 22:38:31 -0700 (PDT)",
        "From": "Yuanhan Liu <yliu@fridaylinux.org>",
        "To": "dev@openvswitch.org",
        "Date": "Tue, 26 Sep 2017 13:36:32 +0800",
        "Message-Id": "<1506404199-23579-3-git-send-email-yliu@fridaylinux.org>",
        "X-Mailer": "git-send-email 2.7.4",
        "In-Reply-To": "<1506404199-23579-1-git-send-email-yliu@fridaylinux.org>",
        "References": "<1506404199-23579-1-git-send-email-yliu@fridaylinux.org>",
        "X-Spam-Status": "No, score=0.0 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,\n\tRCVD_IN_DNSWL_NONE autolearn=disabled version=3.3.1",
        "X-Spam-Checker-Version": "SpamAssassin 3.3.1 (2010-03-16) on\n\tsmtp1.linux-foundation.org",
        "Cc": "Simon Horman <simon.horman@netronome.com>",
        "Subject": "[ovs-dev] [PATCH v3 2/9] dpif-netdev: retrieve flow directly from\n\tthe flow mark",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.12",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n\t<mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Sender": "ovs-dev-bounces@openvswitch.org",
        "Errors-To": "ovs-dev-bounces@openvswitch.org"
    },
    "content": "So that we could skip the heavy emc processing, notably,\nthe miniflow_extract function. A simple PHY-PHY forwarding\ntesting (with one core, one queue and one flow) shows about\n70% performance improvement.\n\nThe retrievement is done at datapath, which should be light.\nUnfortunately, the pthread lock is too heavy for that. Instead,\nRCU is chosen.\n\nThe major race issue is that the flows array might have been\nresized, aka, the address might have been changed. For example,\nassume the size of the flows array is 4. And then it's resized\nto 8. Then a thread might reference the 6th item of the old\narray, which is wrong.\n\nRCU firstly makes sure the flows address update is atomic. Also\nit provides an barrier. Setting the new array size after the\novsrcu_set() makes sure above issue will not happen.\n\nNote that though the heavy miniflow_extract is skipped, we\nstill have to do per packet checking, due to we have to check\nthe tcp_flags.\n\nCo-authored-by: Finn Christensen <fc@napatech.com>\nSigned-off-by: Yuanhan Liu <yliu@fridaylinux.org>\nSigned-off-by: Finn Christensen <fc@napatech.com>\n---\n\nv3: - replace CMAP with array, which futhure improves the\n      performance from 50% to 70%\n\n    - introduce some common help functions for parse_tcp_flags\n\nv2: update tcp_flags, which also fixes the build warnings\n---\n lib/dp-packet.h   |  13 +++++\n lib/dpif-netdev.c |  91 +++++++++++++++++++++++++-------\n lib/flow.c        | 155 +++++++++++++++++++++++++++++++++++++++++++-----------\n lib/flow.h        |   1 +\n 4 files changed, 209 insertions(+), 51 deletions(-)",
    "diff": "diff --git a/lib/dp-packet.h b/lib/dp-packet.h\nindex 046f3ab..a7a062f 100644\n--- a/lib/dp-packet.h\n+++ b/lib/dp-packet.h\n@@ -691,6 +691,19 @@ reset_dp_packet_checksum_ol_flags(struct dp_packet *p)\n #define reset_dp_packet_checksum_ol_flags(arg)\n #endif\n \n+static inline bool\n+dp_packet_has_flow_mark(struct dp_packet *p OVS_UNUSED,\n+                        uint32_t *mark OVS_UNUSED)\n+{\n+#ifdef DPDK_NETDEV\n+    if (p->mbuf.ol_flags & PKT_RX_FDIR_ID) {\n+        *mark = p->mbuf.hash.fdir.hi;\n+        return true;\n+    }\n+#endif\n+    return false;\n+}\n+\n enum { NETDEV_MAX_BURST = 32 }; /* Maximum number packets in a batch. */\n \n struct dp_packet_batch {\ndiff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c\nindex f417b25..5c33c74 100644\n--- a/lib/dpif-netdev.c\n+++ b/lib/dpif-netdev.c\n@@ -1845,7 +1845,7 @@ struct flow_mark_map {\n     size_t max;\n     struct ovs_mutex mutex;\n     struct id_pool *mark_pool;\n-    struct dp_netdev_flow **flows;\n+    OVSRCU_TYPE(struct dp_netdev_flow **) flows;\n };\n \n static struct flow_mark_map flow_mark_map = {\n@@ -1857,16 +1857,20 @@ static bool\n dp_netdev_alloc_flow_mark(uint32_t *mark)\n {\n     bool succeed = true;\n+    struct dp_netdev_flow **flows;\n \n     ovs_mutex_lock(&flow_mark_map.mutex);\n \n     /* Haven't initiated yet, do it here */\n-    if (!flow_mark_map.flows) {\n-        flow_mark_map.flows = xzalloc(sizeof(struct dp_netdev_flow *) *\n-                                      flow_mark_map.max);\n+    if (!flow_mark_map.mark_pool) {\n         flow_mark_map.mark_pool = id_pool_create(0, UINT32_MAX / 2);\n+\n+        flows = xzalloc(sizeof(struct dp_netdev_flow *) * flow_mark_map.max);\n+        ovsrcu_set(&flow_mark_map.flows, flows);\n     }\n \n+    flows = ovsrcu_get_protected(struct dp_netdev_flow **,\n+                                 &flow_mark_map.flows);\n     do {\n         if (!id_pool_alloc_id(flow_mark_map.mark_pool, mark)) {\n             succeed = false;\n@@ -1874,16 +1878,28 @@ dp_netdev_alloc_flow_mark(uint32_t *mark)\n         }\n \n         if (*mark >= flow_mark_map.max) {\n-            flow_mark_map.flows = xrealloc(flow_mark_map.flows,\n-                                           flow_mark_map.max * 2 *\n-                                           sizeof(struct dp_netdev_flow *));\n-            memset(&flow_mark_map.flows[flow_mark_map.max], 0,\n-                   flow_mark_map.max * sizeof(struct dp_netdev_flow *));\n+            struct dp_netdev_flow **new_flows;\n+\n+            new_flows = xmalloc(2 * flow_mark_map.max *\n+                                sizeof(struct dp_netdev_flow *));\n+            memcpy(new_flows, flows, sizeof(struct dp_netdev_flow *) *\n+                                     flow_mark_map.max);\n+            memset(&new_flows[flow_mark_map.max], 0,\n+                   sizeof(struct dp_netdev_flow *) * flow_mark_map.max);\n+            ovsrcu_set(&flow_mark_map.flows, new_flows);\n+            /*\n+             * Set the new size after above ovsrcu_set, which will make\n+             * sure the thread still referencing the old array will not\n+             * go beyond the old max.\n+             */\n             flow_mark_map.max *= 2;\n \n+            ovsrcu_synchronize();\n+            free(flows);\n+            flows = new_flows;\n             break;\n         }\n-    } while (flow_mark_map.flows[*mark]);\n+    } while (flows[*mark]);\n \n     ovs_mutex_unlock(&flow_mark_map.mutex);\n \n@@ -1894,9 +1910,13 @@ static void\n dp_netdev_install_flow_mark_map(const uint32_t mark,\n                                 struct dp_netdev_flow *netdev_flow)\n {\n+    struct dp_netdev_flow **flows;\n+\n     ovs_mutex_lock(&flow_mark_map.mutex);\n-    if (mark < flow_mark_map.max) {\n-        flow_mark_map.flows[mark] = netdev_flow;\n+    if (OVS_LIKELY(mark < flow_mark_map.max)) {\n+        flows = ovsrcu_get_protected(struct dp_netdev_flow **,\n+                                     &flow_mark_map.flows);\n+        flows[mark] = netdev_flow;\n     }\n     ovs_mutex_unlock(&flow_mark_map.mutex);\n }\n@@ -1904,14 +1924,32 @@ dp_netdev_install_flow_mark_map(const uint32_t mark,\n static void\n dp_netdev_remove_flow_mark_map(const uint32_t mark)\n {\n+    struct dp_netdev_flow **flows;\n+\n     ovs_mutex_lock(&flow_mark_map.mutex);\n     id_pool_free_id(flow_mark_map.mark_pool, mark);\n     if (mark < flow_mark_map.max) {\n-        flow_mark_map.flows[mark] = NULL;\n+        flows = ovsrcu_get_protected(struct dp_netdev_flow **,\n+                                     &flow_mark_map.flows);\n+        flows[mark] = NULL;\n     }\n     ovs_mutex_unlock(&flow_mark_map.mutex);\n }\n \n+static struct dp_netdev_flow *\n+dp_netdev_pmd_find_flow_by_mark(const uint32_t mark)\n+{\n+    struct dp_netdev_flow **flows;\n+\n+    if (OVS_LIKELY(mark < flow_mark_map.max)) {\n+        flows = ovsrcu_get(struct dp_netdev_flow **, &flow_mark_map.flows);\n+        return flows[mark];\n+    }\n+\n+    return NULL;\n+}\n+\n+\n static void\n dp_netdev_pmd_remove_flow(struct dp_netdev_pmd_thread *pmd,\n                           struct dp_netdev_flow *flow)\n@@ -4938,10 +4976,10 @@ struct packet_batch_per_flow {\n static inline void\n packet_batch_per_flow_update(struct packet_batch_per_flow *batch,\n                              struct dp_packet *packet,\n-                             const struct miniflow *mf)\n+                             uint16_t tcp_flags)\n {\n     batch->byte_count += dp_packet_size(packet);\n-    batch->tcp_flags |= miniflow_get_tcp_flags(mf);\n+    batch->tcp_flags |= tcp_flags;\n     batch->array.packets[batch->array.count++] = packet;\n }\n \n@@ -4976,7 +5014,7 @@ packet_batch_per_flow_execute(struct packet_batch_per_flow *batch,\n \n static inline void\n dp_netdev_queue_batches(struct dp_packet *pkt,\n-                        struct dp_netdev_flow *flow, const struct miniflow *mf,\n+                        struct dp_netdev_flow *flow, uint16_t tcp_flags,\n                         struct packet_batch_per_flow *batches,\n                         size_t *n_batches)\n {\n@@ -4987,7 +5025,7 @@ dp_netdev_queue_batches(struct dp_packet *pkt,\n         packet_batch_per_flow_init(batch, flow);\n     }\n \n-    packet_batch_per_flow_update(batch, pkt, mf);\n+    packet_batch_per_flow_update(batch, pkt, tcp_flags);\n }\n \n /* Try to process all ('cnt') the 'packets' using only the exact match cache\n@@ -5015,11 +5053,13 @@ emc_processing(struct dp_netdev_pmd_thread *pmd,\n     const size_t size = dp_packet_batch_size(packets_);\n     uint32_t cur_min;\n     int i;\n+    uint16_t tcp_flags;\n \n     atomic_read_relaxed(&pmd->dp->emc_insert_min, &cur_min);\n \n     DP_PACKET_BATCH_REFILL_FOR_EACH (i, size, packet, packets_) {\n         struct dp_netdev_flow *flow;\n+        uint32_t flow_mark;\n \n         if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {\n             dp_packet_delete(packet);\n@@ -5027,6 +5067,16 @@ emc_processing(struct dp_netdev_pmd_thread *pmd,\n             continue;\n         }\n \n+        if (dp_packet_has_flow_mark(packet, &flow_mark)) {\n+            flow = dp_netdev_pmd_find_flow_by_mark(flow_mark);\n+            if (flow) {\n+                tcp_flags = parse_tcp_flags(packet);\n+                dp_netdev_queue_batches(packet, flow, tcp_flags, batches,\n+                                        n_batches);\n+                continue;\n+            }\n+        }\n+\n         if (i != size - 1) {\n             struct dp_packet **packets = packets_->packets;\n             /* Prefetch next packet data and metadata. */\n@@ -5044,7 +5094,8 @@ emc_processing(struct dp_netdev_pmd_thread *pmd,\n         /* If EMC is disabled skip emc_lookup */\n         flow = (cur_min == 0) ? NULL: emc_lookup(flow_cache, key);\n         if (OVS_LIKELY(flow)) {\n-            dp_netdev_queue_batches(packet, flow, &key->mf, batches,\n+            tcp_flags = miniflow_get_tcp_flags(&key->mf);\n+            dp_netdev_queue_batches(packet, flow, tcp_flags, batches,\n                                     n_batches);\n         } else {\n             /* Exact match cache missed. Group missed packets together at\n@@ -5221,7 +5272,9 @@ fast_path_processing(struct dp_netdev_pmd_thread *pmd,\n         flow = dp_netdev_flow_cast(rules[i]);\n \n         emc_probabilistic_insert(pmd, &keys[i], flow);\n-        dp_netdev_queue_batches(packet, flow, &keys[i].mf, batches, n_batches);\n+        dp_netdev_queue_batches(packet, flow,\n+                                miniflow_get_tcp_flags(&keys[i].mf),\n+                                batches, n_batches);\n     }\n \n     dp_netdev_count_packet(pmd, DP_STAT_MASKED_HIT, cnt - miss_cnt);\ndiff --git a/lib/flow.c b/lib/flow.c\nindex b2b10aa..df2b879 100644\n--- a/lib/flow.c\n+++ b/lib/flow.c\n@@ -626,6 +626,70 @@ flow_extract(struct dp_packet *packet, struct flow *flow)\n     miniflow_expand(&m.mf, flow);\n }\n \n+static inline bool\n+ipv4_sanity_check(const struct ip_header *nh, size_t size,\n+                  int *ip_lenp, uint16_t *tot_lenp)\n+{\n+    int ip_len;\n+    uint16_t tot_len;\n+\n+    if (OVS_UNLIKELY(size < IP_HEADER_LEN)) {\n+        return false;\n+    }\n+    ip_len = IP_IHL(nh->ip_ihl_ver) * 4;\n+\n+    if (OVS_UNLIKELY(ip_len < IP_HEADER_LEN || size < ip_len)) {\n+        return false;\n+    }\n+\n+    tot_len = ntohs(nh->ip_tot_len);\n+    if (OVS_UNLIKELY(tot_len > size || ip_len > tot_len ||\n+                size - tot_len > UINT8_MAX)) {\n+        return false;\n+    }\n+\n+    *ip_lenp = ip_len;\n+    *tot_lenp = tot_len;\n+\n+    return true;\n+}\n+\n+static inline uint8_t\n+ipv4_get_nw_frag(const struct ip_header *nh)\n+{\n+    uint8_t nw_frag = 0;\n+\n+    if (OVS_UNLIKELY(IP_IS_FRAGMENT(nh->ip_frag_off))) {\n+        nw_frag = FLOW_NW_FRAG_ANY;\n+        if (nh->ip_frag_off & htons(IP_FRAG_OFF_MASK)) {\n+            nw_frag |= FLOW_NW_FRAG_LATER;\n+        }\n+    }\n+\n+    return nw_frag;\n+}\n+\n+static inline bool\n+ipv6_sanity_check(const struct ovs_16aligned_ip6_hdr *nh, size_t size)\n+{\n+    uint16_t plen;\n+\n+    if (OVS_UNLIKELY(size < sizeof *nh)) {\n+        return false;\n+    }\n+\n+    plen = ntohs(nh->ip6_plen);\n+    if (OVS_UNLIKELY(plen > size)) {\n+        return false;\n+    }\n+    /* Jumbo Payload option not supported yet. */\n+    if (OVS_UNLIKELY(size - plen > UINT8_MAX)) {\n+        return false;\n+    }\n+\n+    return true;\n+}\n+\n /* Caller is responsible for initializing 'dst' with enough storage for\n  * FLOW_U64S * 8 bytes. */\n void\n@@ -750,22 +814,7 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)\n         int ip_len;\n         uint16_t tot_len;\n \n-        if (OVS_UNLIKELY(size < IP_HEADER_LEN)) {\n-            goto out;\n-        }\n-        ip_len = IP_IHL(nh->ip_ihl_ver) * 4;\n-\n-        if (OVS_UNLIKELY(ip_len < IP_HEADER_LEN)) {\n-            goto out;\n-        }\n-        if (OVS_UNLIKELY(size < ip_len)) {\n-            goto out;\n-        }\n-        tot_len = ntohs(nh->ip_tot_len);\n-        if (OVS_UNLIKELY(tot_len > size || ip_len > tot_len)) {\n-            goto out;\n-        }\n-        if (OVS_UNLIKELY(size - tot_len > UINT8_MAX)) {\n+        if (OVS_UNLIKELY(!ipv4_sanity_check(nh, size, &ip_len, &tot_len))) {\n             goto out;\n         }\n         dp_packet_set_l2_pad_size(packet, size - tot_len);\n@@ -788,31 +837,19 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)\n         nw_tos = nh->ip_tos;\n         nw_ttl = nh->ip_ttl;\n         nw_proto = nh->ip_proto;\n-        if (OVS_UNLIKELY(IP_IS_FRAGMENT(nh->ip_frag_off))) {\n-            nw_frag = FLOW_NW_FRAG_ANY;\n-            if (nh->ip_frag_off & htons(IP_FRAG_OFF_MASK)) {\n-                nw_frag |= FLOW_NW_FRAG_LATER;\n-            }\n-        }\n+        nw_frag = ipv4_get_nw_frag(nh);\n         data_pull(&data, &size, ip_len);\n     } else if (dl_type == htons(ETH_TYPE_IPV6)) {\n-        const struct ovs_16aligned_ip6_hdr *nh;\n+        const struct ovs_16aligned_ip6_hdr *nh = data;\n         ovs_be32 tc_flow;\n         uint16_t plen;\n \n-        if (OVS_UNLIKELY(size < sizeof *nh)) {\n+        if (OVS_UNLIKELY(!ipv6_sanity_check(nh, size))) {\n             goto out;\n         }\n-        nh = data_pull(&data, &size, sizeof *nh);\n+        data_pull(&data, &size, sizeof *nh);\n \n         plen = ntohs(nh->ip6_plen);\n-        if (OVS_UNLIKELY(plen > size)) {\n-            goto out;\n-        }\n-        /* Jumbo Payload option not supported yet. */\n-        if (OVS_UNLIKELY(size - plen > UINT8_MAX)) {\n-            goto out;\n-        }\n         dp_packet_set_l2_pad_size(packet, size - plen);\n         size = plen;   /* Never pull padding. */\n \n@@ -991,6 +1028,60 @@ parse_dl_type(const struct eth_header *data_, size_t size)\n     return parse_ethertype(&data, &size);\n }\n \n+uint16_t\n+parse_tcp_flags(struct dp_packet *packet)\n+{\n+    const void *data = dp_packet_data(packet);\n+    size_t size = dp_packet_size(packet);\n+    ovs_be16 dl_type;\n+    uint8_t nw_frag = 0, nw_proto = 0;\n+\n+    if (packet->packet_type != htonl(PT_ETH)) {\n+        return 0;\n+    }\n+\n+    data_pull(&data, &size, ETH_ADDR_LEN * 2);\n+    dl_type = parse_ethertype(&data, &size);\n+    if (OVS_LIKELY(dl_type == htons(ETH_TYPE_IP))) {\n+        const struct ip_header *nh = data;\n+        int ip_len;\n+        uint16_t tot_len;\n+\n+        if (OVS_UNLIKELY(!ipv4_sanity_check(nh, size, &ip_len, &tot_len))) {\n+            return 0;\n+        }\n+        nw_proto = nh->ip_proto;\n+        nw_frag = ipv4_get_nw_frag(nh);\n+\n+        size = tot_len;   /* Never pull padding. */\n+        data_pull(&data, &size, ip_len);\n+    } else if (dl_type == htons(ETH_TYPE_IPV6)) {\n+        const struct ovs_16aligned_ip6_hdr *nh = data;\n+\n+        if (OVS_UNLIKELY(!ipv6_sanity_check(nh, size))) {\n+            return 0;\n+        }\n+        data_pull(&data, &size, sizeof *nh);\n+\n+        size = ntohs(nh->ip6_plen); /* Never pull padding. */\n+        if (!parse_ipv6_ext_hdrs__(&data, &size, &nw_proto, &nw_frag)) {\n+            return 0;\n+        }\n+        nw_proto = nh->ip6_nxt;\n+    } else {\n+        return 0;\n+    }\n+\n+    if (!(nw_frag & FLOW_NW_FRAG_LATER) && nw_proto == IPPROTO_TCP &&\n+        size >= TCP_HEADER_LEN) {\n+        const struct tcp_header *tcp = data;\n+\n+        return TCP_FLAGS_BE32(tcp->tcp_ctl);\n+    }\n+\n+    return 0;\n+}\n+\n /* For every bit of a field that is wildcarded in 'wildcards', sets the\n  * corresponding bit in 'flow' to zero. */\n void\ndiff --git a/lib/flow.h b/lib/flow.h\nindex 6ae5a67..f113ec4 100644\n--- a/lib/flow.h\n+++ b/lib/flow.h\n@@ -130,6 +130,7 @@ bool parse_ipv6_ext_hdrs(const void **datap, size_t *sizep, uint8_t *nw_proto,\n                          uint8_t *nw_frag);\n ovs_be16 parse_dl_type(const struct eth_header *data_, size_t size);\n bool parse_nsh(const void **datap, size_t *sizep, struct flow_nsh *key);\n+uint16_t parse_tcp_flags(struct dp_packet *packet);\n \n static inline uint64_t\n flow_get_xreg(const struct flow *flow, int idx)\n",
    "prefixes": [
        "ovs-dev",
        "v3",
        "2/9"
    ]
}