get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2196877,
    "url": "http://patchwork.ozlabs.org/api/patches/2196877/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/0245e3e0d6bd04c7fd3256b12a5eb40c44617b5e.1771241734.git.echaudro@redhat.com/",
    "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": "<0245e3e0d6bd04c7fd3256b12a5eb40c44617b5e.1771241734.git.echaudro@redhat.com>",
    "list_archive_url": null,
    "date": "2026-02-16T11:35:34",
    "name": "[ovs-dev,RFC] dpif-offload: Add support for UDP tunnel entropy.",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "1b5546b718f5c1264143b241fd6d7708848b039d",
    "submitter": {
        "id": 70613,
        "url": "http://patchwork.ozlabs.org/api/people/70613/?format=api",
        "name": "Eelco Chaudron",
        "email": "echaudro@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/0245e3e0d6bd04c7fd3256b12a5eb40c44617b5e.1771241734.git.echaudro@redhat.com/mbox/",
    "series": [
        {
            "id": 492296,
            "url": "http://patchwork.ozlabs.org/api/series/492296/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/openvswitch/list/?series=492296",
            "date": "2026-02-16T11:35:34",
            "name": "[ovs-dev,RFC] dpif-offload: Add support for UDP tunnel entropy.",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/492296/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2196877/comments/",
    "check": "fail",
    "checks": "http://patchwork.ozlabs.org/api/patches/2196877/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@legolas.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=OeclScKP;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::133; helo=smtp2.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)",
            "smtp2.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=OeclScKP",
            "smtp1.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com",
            "smtp1.osuosl.org;\n dkim=fail reason=\"signature verification failed\" (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=OeclScKP"
        ],
        "Received": [
            "from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fF11N2x1wz1xpY\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 16 Feb 2026 22:35:52 +1100 (AEDT)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp2.osuosl.org (Postfix) with ESMTP id 7D8C2406EE;\n\tMon, 16 Feb 2026 11:35:50 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id kCQ95qLj3_Hf; Mon, 16 Feb 2026 11:35:48 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56])\n\tby smtp2.osuosl.org (Postfix) with ESMTPS id AC48D4018D;\n\tMon, 16 Feb 2026 11:35:48 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 60E89C0035;\n\tMon, 16 Feb 2026 11:35:48 +0000 (UTC)",
            "from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::138])\n by lists.linuxfoundation.org (Postfix) with ESMTP id BF089C0033\n for <dev@openvswitch.org>; Mon, 16 Feb 2026 11:35:46 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp1.osuosl.org (Postfix) with ESMTP id 984B782BBA\n for <dev@openvswitch.org>; Mon, 16 Feb 2026 11:35:46 +0000 (UTC)",
            "from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id fCP-0_7GFS8N for <dev@openvswitch.org>;\n Mon, 16 Feb 2026 11:35:44 +0000 (UTC)",
            "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.133.124])\n by smtp1.osuosl.org (Postfix) with ESMTPS id 56B6D82BA1\n for <dev@openvswitch.org>; Mon, 16 Feb 2026 11:35:43 +0000 (UTC)",
            "from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-180-KPy2tErsO-iJQ2F8fTb3Mg-1; Mon,\n 16 Feb 2026 06:35:40 -0500",
            "from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 9E2B4195608A; Mon, 16 Feb 2026 11:35:39 +0000 (UTC)",
            "from ebuild.chome (unknown [10.45.226.100])\n by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 3399C19560B8; Mon, 16 Feb 2026 11:35:37 +0000 (UTC)"
        ],
        "X-Virus-Scanned": [
            "amavis at osuosl.org",
            "amavis at osuosl.org"
        ],
        "X-Comment": "SPF check N/A for local connections - client-ip=140.211.9.56;\n helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ",
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 smtp2.osuosl.org AC48D4018D",
            "OpenDKIM Filter v2.11.0 smtp1.osuosl.org 56B6D82BA1"
        ],
        "Received-SPF": "Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124;\n helo=us-smtp-delivery-124.mimecast.com; envelope-from=echaudro@redhat.com;\n receiver=<UNKNOWN>",
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 smtp1.osuosl.org 56B6D82BA1",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1771241742;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding;\n bh=Z3FZZ45Rdp8yxYmSjzjXjmCub7odF7lRqv0B2jYtRtg=;\n b=OeclScKPsPwov6cnzvbES6oIvzqq4IOqoWeSGMk8WYKkFRGQh7pVxeTNE5ZpmDpActCIiO\n 6F7ixyOy8JWSbHaCAc8XsuZhfnNmw2nVU5GD1F3x+YaJwvaYYAFOPYpQvSMpUb4Rri3oAi\n FDB6a1Q3vH/S08NCMlp1kI+QOxelE40=",
        "X-MC-Unique": "KPy2tErsO-iJQ2F8fTb3Mg-1",
        "X-Mimecast-MFC-AGG-ID": "KPy2tErsO-iJQ2F8fTb3Mg_1771241739",
        "To": "dev@openvswitch.org",
        "Date": "Mon, 16 Feb 2026 12:35:34 +0100",
        "Message-ID": "\n <0245e3e0d6bd04c7fd3256b12a5eb40c44617b5e.1771241734.git.echaudro@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.0 on 10.30.177.12",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-MFC-PROC-ID": "-CJpdZaZMn6dwnuXHsie_WYLEKyZhNcP1LO_Ldk7HIg_1771241739",
        "X-Mimecast-Originator": "redhat.com",
        "Subject": "[ovs-dev] [RFC PATCH] dpif-offload: Add support for UDP tunnel\n entropy.",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <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 <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "From": "Eelco Chaudron via dev <ovs-dev@openvswitch.org>",
        "Reply-To": "Eelco Chaudron <echaudro@redhat.com>",
        "Cc": "elibr@nvidia.com",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "This patch introduces a new API to the offload provider framework that\nallows hardware offload implementations to control UDP tunnel source port\nselection during tunnel encapsulation.\n\nBackground and Motivation\n==========================\n\nUDP-based tunnels (VXLAN, Geneve, etc.) use the UDP source port as an\nentropy field to enable ECMP load balancing across multiple paths in the\nnetwork. The source port is typically calculated by hashing packet header\nfields (5-tuple or inner packet headers) to distribute flows across\ndifferent paths.\n\nHowever, hardware offload implementations may require different approaches\nto source port calculation:\n\n1. Hardware NICs may use different hash functions or hash inputs than\n   the software datapath, which can lead to inconsistent flow distribution\n   when mixing hardware and software paths.\n\n2. Some hardware may support enhanced entropy mechanisms (e.g., using\n   additional packet fields or hardware-specific hash engines) that provide\n   better load distribution than the default software implementation.\n\nDesign\n======\n\nThis patch adds a new optional callback to the dpif_offload_class:\n\n  bool (*netdev_udp_tnl_get_src_port)(const struct dpif_offload *,\n                                      const struct netdev *egress_port,\n                                      const struct netdev *tunnel_port,\n                                      const struct dp_packet *packet,\n                                      ovs_be16 *src_port);\n\nThe callback is invoked during tunnel push operations when hardware offload\nis enabled and the egress port is known. It receives:\n  - egress_port: The physical port where the encapsulated packet will egress\n  - tunnel_port: The tunnel netdev performing the encapsulation\n  - packet: The inner packet to be encapsulated\n\nIf the provider implements this callback and returns true, the returned\nsrc_port value is used. Otherwise, OVS falls back to the standard hash-based\nsource port calculation.\n\nNOTE: This patch needs to be applied on top of the patch series below:\n      https://patchwork.ozlabs.org/project/openvswitch/list/?series=491639\n\nSigned-off-by: Eelco Chaudron <echaudro@redhat.com>\n---\n lib/dpif-netdev.c           |  15 +++-\n lib/dpif-offload-dummy.c    | 134 +++++++++++++++++++++++++++++++++++-\n lib/dpif-offload-provider.h |  14 ++++\n lib/dpif-offload.c          |  20 ++++++\n lib/dpif-offload.h          |   4 ++\n lib/netdev-native-tnl.c     |  13 +++-\n lib/netdev-native-tnl.h     |   5 ++\n lib/netdev-provider.h       |   9 ++-\n lib/netdev.c                |   4 +-\n lib/netdev.h                |   1 +\n tests/dpif-netdev.at        | 101 ++++++++++++++++++++++++---\n 11 files changed, 299 insertions(+), 21 deletions(-)",
    "diff": "diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c\nindex 64531a02c0..d46cfe8bec 100644\n--- a/lib/dpif-netdev.c\n+++ b/lib/dpif-netdev.c\n@@ -8374,8 +8374,9 @@ push_tnl_action(const struct dp_netdev_pmd_thread *pmd,\n                 const struct nlattr *attr,\n                 struct dp_packet_batch *batch)\n {\n-    struct tx_port *tun_port;\n     const struct ovs_action_push_tnl *data;\n+    const struct netdev *output = NULL;\n+    struct tx_port *tun_port;\n     int err;\n \n     data = nl_attr_get(attr);\n@@ -8385,7 +8386,17 @@ push_tnl_action(const struct dp_netdev_pmd_thread *pmd,\n         err = -EINVAL;\n         goto error;\n     }\n-    err = netdev_push_header(tun_port->port->netdev, batch, data);\n+\n+    if (dpif_offload_enabled()) {\n+        struct tx_port *out_port = pmd_send_port_cache_lookup(\n+                                       pmd, data->out_port);\n+\n+        if (out_port) {\n+            output = out_port->port->netdev;\n+        }\n+    }\n+\n+    err = netdev_push_header(tun_port->port->netdev, output, batch, data);\n     if (!err) {\n         return 0;\n     }\ndiff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c\nindex 3dd9eff14b..bf9084076a 100644\n--- a/lib/dpif-offload-dummy.c\n+++ b/lib/dpif-offload-dummy.c\n@@ -24,6 +24,7 @@\n #include \"dummy.h\"\n #include \"id-fpool.h\"\n #include \"netdev-provider.h\"\n+#include \"netdev-native-tnl.h\"\n #include \"odp-util.h\"\n #include \"util.h\"\n #include \"uuid.h\"\n@@ -613,6 +614,36 @@ dummy_offload_hw_post_process(const struct dpif_offload *offload_,\n     return 0;\n }\n \n+static ovs_be16\n+dummy_offload_udp_tnl_get_src_port__(const struct dp_packet *packet)\n+{\n+    /* Use FNV-1a hash to ensure consistent results across all platforms.  The\n+     * standard OVS hash functions have architecture-specific implementations\n+     * (SSE4.2, ARM64 optimizations, etc.) that produce different outputs for\n+     * identical inputs, making tests non-deterministic. */\n+    const uint8_t *data = dp_packet_data(packet);\n+    size_t len = dp_packet_size(packet);\n+    uint32_t hash = 2166136261U;\n+    uint32_t prime = 16777619U;\n+\n+    for (size_t i = 0; i < len; i++) {\n+        hash ^= data[i];\n+        hash *= prime;\n+    }\n+    return htons((uint16_t) hash);\n+}\n+\n+static bool\n+dummy_offload_udp_tnl_get_src_port(\n+    const struct dpif_offload *offload OVS_UNUSED,\n+    const struct netdev *egress_port OVS_UNUSED,\n+    const struct netdev *tunnel_port OVS_UNUSED,\n+    const struct dp_packet *packet, ovs_be16 *src_port)\n+{\n+    *src_port = dummy_offload_udp_tnl_get_src_port__(packet);\n+    return true;\n+}\n+\n static bool\n dummy_offload_are_all_actions_supported(const struct dpif_offload *offload_,\n                                         odp_port_t in_odp,\n@@ -631,7 +662,10 @@ dummy_offload_are_all_actions_supported(const struct dpif_offload *offload_,\n      * that they provide full protection when calling netdev_send() from any\n      * thread, via a netdev-level mutex. */\n     NL_ATTR_FOR_EACH (nla, left, actions, actions_len) {\n-        if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {\n+        enum ovs_action_attr action = nl_attr_type(nla);\n+\n+        switch (action) {\n+        case OVS_ACTION_ATTR_OUTPUT: {\n             odp_port_t out_odp = nl_attr_get_odp_port(nla);\n             struct dummy_offload_port *out_port;\n \n@@ -641,7 +675,49 @@ dummy_offload_are_all_actions_supported(const struct dpif_offload *offload_,\n                           netdev_get_type(out_port->pm_port.netdev))) {\n                 return false;\n             }\n-        } else {\n+            break;\n+        }\n+\n+        case OVS_ACTION_ATTR_TUNNEL_PUSH: {\n+            /* We only support UDP tunnels, i.e. VXLAN and Geneve. */\n+            const struct ovs_action_push_tnl *data = nl_attr_get(nla);\n+\n+            if (data->tnl_type != OVS_VPORT_TYPE_VXLAN\n+                && data->tnl_type != OVS_VPORT_TYPE_GENEVE) {\n+                return false;\n+            }\n+            break;\n+        }\n+\n+        case OVS_ACTION_ATTR_UNSPEC:\n+        case OVS_ACTION_ATTR_USERSPACE:\n+        case OVS_ACTION_ATTR_SET:\n+        case OVS_ACTION_ATTR_PUSH_VLAN:\n+        case OVS_ACTION_ATTR_POP_VLAN:\n+        case OVS_ACTION_ATTR_SAMPLE:\n+        case OVS_ACTION_ATTR_RECIRC:\n+        case OVS_ACTION_ATTR_HASH:\n+        case OVS_ACTION_ATTR_PUSH_MPLS:\n+        case OVS_ACTION_ATTR_POP_MPLS:\n+        case OVS_ACTION_ATTR_SET_MASKED:\n+        case OVS_ACTION_ATTR_CT:\n+        case OVS_ACTION_ATTR_TRUNC:\n+        case OVS_ACTION_ATTR_PUSH_ETH:\n+        case OVS_ACTION_ATTR_POP_ETH:\n+        case OVS_ACTION_ATTR_CT_CLEAR:\n+        case OVS_ACTION_ATTR_PUSH_NSH:\n+        case OVS_ACTION_ATTR_POP_NSH:\n+        case OVS_ACTION_ATTR_METER:\n+        case OVS_ACTION_ATTR_CLONE:\n+        case OVS_ACTION_ATTR_CHECK_PKT_LEN:\n+        case OVS_ACTION_ATTR_ADD_MPLS:\n+        case OVS_ACTION_ATTR_DEC_TTL:\n+        case OVS_ACTION_ATTR_DROP:\n+        case OVS_ACTION_ATTR_PSAMPLE:\n+        case OVS_ACTION_ATTR_TUNNEL_POP:\n+        case OVS_ACTION_ATTR_LB_OUTPUT:\n+        case __OVS_ACTION_ATTR_MAX:\n+        default:\n             return false;\n         }\n     }\n@@ -664,8 +740,10 @@ dummy_offload_hw_process_pkt(const struct dpif_offload *offload_,\n \n     NL_ATTR_FOR_EACH (nla, left, flow->actions, flow->actions_len) {\n         bool last_action = (left <= NLA_ALIGN(nla->nla_len));\n+        enum ovs_action_attr action = nl_attr_type(nla);\n \n-        if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {\n+        switch (action) {\n+        case OVS_ACTION_ATTR_OUTPUT: {\n             odp_port_t odp_port = nl_attr_get_odp_port(nla);\n             struct dummy_offload_port *port;\n             struct dp_packet_batch batch;\n@@ -684,6 +762,55 @@ dummy_offload_hw_process_pkt(const struct dpif_offload *offload_,\n              * for now we assume hash steering based on the number of queues\n              * configured for the dummy-netdev. */\n              netdev_send(port->pm_port.netdev, hash % n_txq, &batch, false);\n+             break;\n+        }\n+        case OVS_ACTION_ATTR_TUNNEL_PUSH: {\n+            const struct ovs_action_push_tnl *data = nl_attr_get(nla);\n+            struct udp_header *udp;\n+            struct flow ovs_flow;\n+            ovs_be16 src_port;\n+\n+            src_port = dummy_offload_udp_tnl_get_src_port__(pkt);\n+            netdev_tnl_push_udp_header(NULL, NULL, pkt, data);\n+\n+            flow_extract(pkt, &ovs_flow);\n+            udp = dp_packet_l4(pkt);\n+            ovs_assert(ovs_flow.nw_proto == IPPROTO_UDP && udp);\n+\n+            udp->udp_src = src_port;\n+            break;\n+        }\n+\n+        case OVS_ACTION_ATTR_UNSPEC:\n+        case OVS_ACTION_ATTR_USERSPACE:\n+        case OVS_ACTION_ATTR_SET:\n+        case OVS_ACTION_ATTR_PUSH_VLAN:\n+        case OVS_ACTION_ATTR_POP_VLAN:\n+        case OVS_ACTION_ATTR_SAMPLE:\n+        case OVS_ACTION_ATTR_RECIRC:\n+        case OVS_ACTION_ATTR_HASH:\n+        case OVS_ACTION_ATTR_PUSH_MPLS:\n+        case OVS_ACTION_ATTR_POP_MPLS:\n+        case OVS_ACTION_ATTR_SET_MASKED:\n+        case OVS_ACTION_ATTR_CT:\n+        case OVS_ACTION_ATTR_TRUNC:\n+        case OVS_ACTION_ATTR_PUSH_ETH:\n+        case OVS_ACTION_ATTR_POP_ETH:\n+        case OVS_ACTION_ATTR_CT_CLEAR:\n+        case OVS_ACTION_ATTR_PUSH_NSH:\n+        case OVS_ACTION_ATTR_POP_NSH:\n+        case OVS_ACTION_ATTR_METER:\n+        case OVS_ACTION_ATTR_CLONE:\n+        case OVS_ACTION_ATTR_CHECK_PKT_LEN:\n+        case OVS_ACTION_ATTR_ADD_MPLS:\n+        case OVS_ACTION_ATTR_DEC_TTL:\n+        case OVS_ACTION_ATTR_DROP:\n+        case OVS_ACTION_ATTR_PSAMPLE:\n+        case OVS_ACTION_ATTR_TUNNEL_POP:\n+        case OVS_ACTION_ATTR_LB_OUTPUT:\n+        case __OVS_ACTION_ATTR_MAX:\n+        default:\n+            OVS_NOT_REACHED();\n         }\n     }\n \n@@ -1062,6 +1189,7 @@ dummy_pmd_thread_lifecycle(const struct dpif_offload *dpif_offload,\n         .port_del = dummy_offload_port_del,                                 \\\n         .get_netdev = dummy_offload_get_netdev,                             \\\n         .netdev_hw_post_process = dummy_offload_hw_post_process,            \\\n+        .netdev_udp_tnl_get_src_port = dummy_offload_udp_tnl_get_src_port,  \\\n         .netdev_flow_put = dummy_flow_put,                                  \\\n         .netdev_flow_del = dummy_flow_del,                                  \\\n         .netdev_flow_stats = dummy_flow_stats,                              \\\ndiff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h\nindex 259de2c299..41e32fe3d7 100644\n--- a/lib/dpif-offload-provider.h\n+++ b/lib/dpif-offload-provider.h\n@@ -279,6 +279,20 @@ struct dpif_offload_class {\n                                   unsigned pmd_id, struct dp_packet *,\n                                   void **flow_reference);\n \n+    /* Allows the offload provider to override the default UDP tunnel source\n+     * port selection.  Called during tunnel encapsulation to determine the\n+     * source port for UDP-based tunnels (VXLAN, Geneve, etc.).\n+     *\n+     * If implemented, should return true and set 'src_port' to the desired\n+     * source port value.  If not implemented or if default behavior is\n+     * desired, should return false to use the standard source port\n+     * calculation. */\n+    bool (*netdev_udp_tnl_get_src_port)(const struct dpif_offload *,\n+                                        const struct netdev *egress_port,\n+                                        const struct netdev *tunnel_port,\n+                                        const struct dp_packet *packet,\n+                                        ovs_be16 *src_port);\n+\n     /* Add or modify the specified flow directly in the offload datapath.\n      * The actual implementation may choose to handle the offload\n      * asynchronously by returning EINPROGRESS and invoking the supplied\ndiff --git a/lib/dpif-offload.c b/lib/dpif-offload.c\nindex cbf1f6c704..00cdd45787 100644\n--- a/lib/dpif-offload.c\n+++ b/lib/dpif-offload.c\n@@ -1465,6 +1465,26 @@ dpif_offload_netdev_hw_post_process(struct netdev *netdev, unsigned pmd_id,\n     return rc;\n }\n \n+bool\n+dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *egress_port,\n+                                         const struct netdev *tunnel_port,\n+                                         const struct dp_packet *packet,\n+                                         ovs_be16 *src_port)\n+{\n+    const struct dpif_offload *offload;\n+\n+    offload = ovsrcu_get(const struct dpif_offload *,\n+                         &egress_port->dpif_offload);\n+\n+    if (OVS_UNLIKELY(!offload)\n+        || !offload->class->netdev_udp_tnl_get_src_port) {\n+        return false;\n+    }\n+\n+    return offload->class->netdev_udp_tnl_get_src_port(\n+               offload, egress_port, tunnel_port, packet, src_port);\n+}\n+\n void\n dpif_offload_datapath_register_flow_unreference_cb(\n     struct dpif *dpif, dpif_offload_flow_unreference_cb *cb)\ndiff --git a/lib/dpif-offload.h b/lib/dpif-offload.h\nindex 0f66d8cd8e..29983eade7 100644\n--- a/lib/dpif-offload.h\n+++ b/lib/dpif-offload.h\n@@ -114,6 +114,10 @@ bool dpif_offload_netdev_same_offload(const struct netdev *,\n int dpif_offload_netdev_hw_post_process(struct netdev *, unsigned pmd_id,\n                                         struct dp_packet *,\n                                         void **flow_reference);\n+bool dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *egress_port,\n+                                              const struct netdev *tunnel_port,\n+                                              const struct dp_packet *,\n+                                              ovs_be16 *src_port);\n \n \f\n /* Callback invoked when a hardware flow offload operation (put/del) completes.\ndiff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c\nindex 008b452b8a..6da5142a16 100644\n--- a/lib/netdev-native-tnl.c\n+++ b/lib/netdev-native-tnl.c\n@@ -36,6 +36,7 @@\n #include \"coverage.h\"\n #include \"csum.h\"\n #include \"dp-packet.h\"\n+#include \"dpif-offload.h\"\n #include \"netdev.h\"\n #include \"netdev-vport.h\"\n #include \"netdev-vport-private.h\"\n@@ -300,7 +301,8 @@ tnl_ol_pop(struct dp_packet *packet, int off)\n }\n \n void\n-netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,\n+netdev_tnl_push_udp_header(const struct netdev *netdev,\n+                           const struct netdev *output_netdev,\n                            struct dp_packet *packet,\n                            const struct ovs_action_push_tnl *data)\n {\n@@ -312,7 +314,10 @@ netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,\n \n     /* We may need to re-calculate the hash and this has to be done before\n      * modifying the packet. */\n-    udp_src = netdev_tnl_get_src_port(packet);\n+    if (!output_netdev || !dpif_offload_netdev_udp_tnl_get_src_port(\n+                               output_netdev, netdev, packet, &udp_src)) {\n+        udp_src = netdev_tnl_get_src_port(packet);\n+    }\n \n     tnl_ol_push(packet, data);\n     udp = netdev_tnl_push_ip_header(packet, data->header, data->header_len,\n@@ -532,6 +537,7 @@ err:\n \n void\n netdev_gre_push_header(const struct netdev *netdev,\n+                       const struct netdev *output_netdev OVS_UNUSED,\n                        struct dp_packet *packet,\n                        const struct ovs_action_push_tnl *data)\n {\n@@ -695,6 +701,7 @@ err:\n \n void\n netdev_erspan_push_header(const struct netdev *netdev,\n+                          const struct netdev *output_netdev OVS_UNUSED,\n                           struct dp_packet *packet,\n                           const struct ovs_action_push_tnl *data)\n {\n@@ -868,6 +875,7 @@ err:\n \n void\n netdev_gtpu_push_header(const struct netdev *netdev,\n+                        const struct netdev *output_netdev OVS_UNUSED,\n                         struct dp_packet *packet,\n                         const struct ovs_action_push_tnl *data)\n {\n@@ -1001,6 +1009,7 @@ netdev_srv6_build_header(const struct netdev *netdev,\n \n void\n netdev_srv6_push_header(const struct netdev *netdev OVS_UNUSED,\n+                        const struct netdev *output_netdev OVS_UNUSED,\n                         struct dp_packet *packet,\n                         const struct ovs_action_push_tnl *data)\n {\ndiff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h\nindex 47d6b6bbcf..7ba8f12fc2 100644\n--- a/lib/netdev-native-tnl.h\n+++ b/lib/netdev-native-tnl.h\n@@ -35,6 +35,7 @@ netdev_gre_build_header(const struct netdev *netdev,\n \n void\n netdev_gre_push_header(const struct netdev *netdev,\n+                       const struct netdev *output_netdev,\n                        struct dp_packet *packet,\n                        const struct ovs_action_push_tnl *data);\n struct dp_packet *\n@@ -47,6 +48,7 @@ netdev_erspan_build_header(const struct netdev *netdev,\n \n void\n netdev_erspan_push_header(const struct netdev *netdev,\n+                          const struct netdev *output_netdev,\n                           struct dp_packet *packet,\n                           const struct ovs_action_push_tnl *data);\n struct dp_packet *\n@@ -57,6 +59,7 @@ netdev_gtpu_pop_header(struct dp_packet *packet);\n \n void\n netdev_gtpu_push_header(const struct netdev *netdev,\n+                        const struct netdev *output_netdev,\n                         struct dp_packet *packet,\n                         const struct ovs_action_push_tnl *data);\n \n@@ -68,6 +71,7 @@ netdev_gtpu_build_header(const struct netdev *netdev,\n struct dp_packet *netdev_srv6_pop_header(struct dp_packet *);\n \n void netdev_srv6_push_header(const struct netdev *,\n+                             const struct netdev *output_netdev,\n                              struct dp_packet *,\n                              const struct ovs_action_push_tnl *);\n \n@@ -77,6 +81,7 @@ int netdev_srv6_build_header(const struct netdev *,\n \n void\n netdev_tnl_push_udp_header(const struct netdev *netdev,\n+                           const struct netdev *output_netdev,\n                            struct dp_packet *packet,\n                            const struct ovs_action_push_tnl *data);\n int\ndiff --git a/lib/netdev-provider.h b/lib/netdev-provider.h\nindex 136d8188c2..36d347777d 100644\n--- a/lib/netdev-provider.h\n+++ b/lib/netdev-provider.h\n@@ -336,8 +336,13 @@ struct netdev_class {\n     /* build_header() can not build entire header for all packets for given\n      * flow.  Push header is called for packet to build header specific to\n      * a packet on actual transmit.  It uses partial header build by\n-     * build_header() which is passed as data. */\n-    void (*push_header)(const struct netdev *,\n+     * build_header() which is passed as data.\n+     *\n+     * The 'output_netdev' points to the egress netdev for the encapsulated\n+     * packet.  This variable is valid only if the egress port is known and\n+     * hardware offload is enabled; otherwise, it will be NULL. */\n+    void (*push_header)(const struct netdev *netdev,\n+                        const struct netdev *output_netdev,\n                         struct dp_packet *packet,\n                         const struct ovs_action_push_tnl *data);\n \ndiff --git a/lib/netdev.c b/lib/netdev.c\nindex daa4287362..a49731d348 100644\n--- a/lib/netdev.c\n+++ b/lib/netdev.c\n@@ -1003,6 +1003,7 @@ int netdev_build_header(const struct netdev *netdev,\n  * that netdev_has_tunnel_push_pop() returns true. */\n int\n netdev_push_header(const struct netdev *netdev,\n+                   const struct netdev *output_netdev,\n                    struct dp_packet_batch *batch,\n                    const struct ovs_action_push_tnl *data)\n {\n@@ -1038,7 +1039,8 @@ netdev_push_header(const struct netdev *netdev,\n                 }\n                 dp_packet_ol_send_prepare(packet, 0);\n             }\n-            netdev->netdev_class->push_header(netdev, packet, data);\n+            netdev->netdev_class->push_header(netdev, output_netdev, packet,\n+                                              data);\n \n             pkt_metadata_init(&packet->md, data->out_port);\n             dp_packet_batch_refill(batch, packet, i);\ndiff --git a/lib/netdev.h b/lib/netdev.h\nindex 40f1621eca..14374ee2d5 100644\n--- a/lib/netdev.h\n+++ b/lib/netdev.h\n@@ -259,6 +259,7 @@ int netdev_build_header(const struct netdev *, struct ovs_action_push_tnl *data,\n                         const struct netdev_tnl_build_header_params *params);\n \n int netdev_push_header(const struct netdev *netdev,\n+                       const struct netdev *output_netdev,\n                        struct dp_packet_batch *,\n                        const struct ovs_action_push_tnl *data);\n void netdev_pop_header(struct netdev *netdev, struct dp_packet_batch *);\ndiff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at\nindex 0480730cab..5b6c961f90 100644\n--- a/tests/dpif-netdev.at\n+++ b/tests/dpif-netdev.at\n@@ -77,6 +77,17 @@ strip_metadata () {\n ]\n m4_divert_pop([PREPARE_TESTS])\n \n+m4_define([CHECK_FWD_PACKET],\n+  [m4_if([$3], [], [], [AT_CHECK([ovs-vsctl set interface $1 options:$3=true])])\n+   AT_CHECK([ovs-appctl netdev-dummy/receive $1 $(cat $4)])\n+   m4_if([$5], [none], [], [\n+     AT_CHECK([awk 1 $5 > expout]) dnl Also normalizes the line endings.\n+     AT_CHECK([ovs-pcap $2.pcap > $2.pcap.txt 2>&1])\n+     AT_CHECK([tail -n 1 $2.pcap.txt], [0], [expout])\n+   ])\n+   m4_if([$3], [], [], [AT_CHECK([ovs-vsctl remove interface $1 options $3])])\n+])\n+\n AT_SETUP([dpif-netdev - netdev-dummy/receive])\n # Create br0 with interfaces p0\n OVS_VSWITCHD_START([add-port br0 p1 -- set interface p1 type=dummy ofport_request=1 -- ])\n@@ -761,6 +772,85 @@ rx_offload_pipe_abort:1\n OVS_VSWITCHD_STOP\n AT_CLEANUP\n \n+AT_SETUP([dpif-netdev - full hw offload - tunnel entropy - dummy-pmd])\n+OVS_VSWITCHD_START(\n+  [add-br br1 -- set bridge br1 datapath-type=dummy -- \\\n+   add-port br1 p1 -- \\\n+       set Interface p1 type=dummy-pmd -- \\\n+   add-br br2 -- set bridge br2 datapath-type=dummy -- \\\n+       set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \\\n+   add-port br2 p2 -- \\\n+       set Interface p2 type=dummy-pmd], [], [], [--dummy-numa 0])\n+\n+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])\n+OVS_WAIT_UNTIL([grep \"Flow HW offload is enabled\" ovs-vswitchd.log])\n+\n+AT_CHECK([ovs-vsctl add-port br1 t1 \\\n+                    -- set Interface t1 type=vxlan \\\n+                       options:remote_ip=1.1.2.92 options:key=123 \\\n+                       options:csum=false ofport_request=11], [0])\n+AT_CHECK([ovs-ofctl add-flow br1 in_port=p1,actions=mod_nw_dst:192.168.1.1,output:11])\n+\n+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK\n+])\n+AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK\n+])\n+AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK\n+])\n+AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg \\\n+            dpif_offload_dummy:file:dbg netdev_dummy:file:dbg])\n+\n+AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap])\n+\n+dnl ICMPv6.\n+AT_DATA([ndp_frame], m4_join([],\n+dnl p = Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01')\n+[3333ff00000104bf1bd82d2d86dd],\n+dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1')\n+[6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001],\n+dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1')\n+[87006e2600000000fe800000000000000000000000000001],\n+dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84')\n+[01018abf7e2f0584]\n+))\n+\n+AT_DATA([ndp_expected], m4_join([],\n+dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01')\n+[aa66aa660001aa66aa6600000800],\n+dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF')\n+[4500007a00004000401133be010102580101025c],\n+dnl p /= UDP(sport=43938, chksum=0)\n+[aba212b500660000],\n+dnl p /= VXLAN(vni=123, flags='Instance')\n+[0800000000007b00],\n+dnl p /= Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01')\n+[3333ff00000104bf1bd82d2d86dd],\n+dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1')\n+[6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001],\n+dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1')\n+[87006e2600000000fe800000000000000000000000000001],\n+dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84')\n+[01018abf7e2f0584]\n+))\n+\n+# Sent two packets, second should be handled in hardware.\n+CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_expected])\n+CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_expected])\n+\n+# Check if we do not hit partial hw offload.\n+AT_CHECK(\n+  [ovs-appctl --format json dpif/offload/show \\\n+     | sed 's/.*\"p1\":{\\([[^}]]*\\)}.*/\\1/; s/,/\\n/g; s/\"//g' \\\n+     | sed -n '/^rx_offload_/p' | sort], [0], [dnl\n+rx_offload_full:1\n+rx_offload_miss:1\n+rx_offload_partial:0\n+rx_offload_pipe_abort:0\n+])\n+\n+OVS_VSWITCHD_STOP\n+AT_CLEANUP\n+\n AT_SETUP([dpif-netdev - check dpctl/add-flow in_port exact match])\n OVS_VSWITCHD_START(\n   [add-port br0 p1 \\\n@@ -877,17 +967,6 @@ AT_CHECK([test `ovs-vsctl get Interface p2 statistics:tx_q0_packets` -gt 0 -a dn\n OVS_VSWITCHD_STOP\n AT_CLEANUP\n \n-m4_define([CHECK_FWD_PACKET],\n-  [m4_if([$3], [], [], [AT_CHECK([ovs-vsctl set interface $1 options:$3=true])])\n-   AT_CHECK([ovs-appctl netdev-dummy/receive $1 $(cat $4)])\n-   m4_if([$5], [none], [], [\n-     AT_CHECK([awk 1 $5 > expout]) dnl Also normalizes the line endings.\n-     AT_CHECK([ovs-pcap $2.pcap > $2.pcap.txt 2>&1])\n-     AT_CHECK([tail -n 1 $2.pcap.txt], [0], [expout])\n-   ])\n-   m4_if([$3], [], [], [AT_CHECK([ovs-vsctl remove interface $1 options $3])])\n-])\n-\n dnl CHECK_IP_CHECKSUMS rx_port tx_port good_pkt bad_pkt good_exp bad_exp\n dnl\n dnl Test combinations of Rx IP checksum flags for a good or bad packet\n",
    "prefixes": [
        "ovs-dev",
        "RFC"
    ]
}