This patch introduces a new API to the offload provider framework that
allows hardware offload implementations to control UDP tunnel source port
selection during tunnel encapsulation.
Background and Motivation
==========================
UDP-based tunnels (VXLAN, Geneve, etc.) use the UDP source port as an
entropy field to enable ECMP load balancing across multiple paths in the
network. The source port is typically calculated by hashing packet header
fields (5-tuple or inner packet headers) to distribute flows across
different paths.
However, hardware offload implementations may require different approaches
to source port calculation:
1. Hardware NICs may use different hash functions or hash inputs than
the software datapath, which can lead to inconsistent flow distribution
when mixing hardware and software paths.
2. Some hardware may support enhanced entropy mechanisms (e.g., using
additional packet fields or hardware-specific hash engines) that provide
better load distribution than the default software implementation.
Design
======
This patch adds a new optional callback to the dpif_offload_class:
bool (*netdev_udp_tnl_get_src_port)(const struct dpif_offload *,
const struct netdev *ingress_netdev,
const struct dp_packet *packet,
ovs_be16 *src_port);
The callback is invoked during tunnel push operations when hardware offload
is enabled and the original ingress port is known. It receives:
- ingress_netdev: The original ingress port where the packet was received
- packet: The inner packet to be encapsulated
If the provider implements this callback and returns true, the returned
src_port value is used. Otherwise, OVS falls back to the standard hash-based
source port calculation.
NOTE: This patch needs to be applied on top of the patch series below:
https://patchwork.ozlabs.org/project/openvswitch/list/?series=491639
Signed-off-by: Eelco Chaudron <[email protected]>
---
lib/dpif-netdev.c | 18 ++++-
lib/dpif-offload-dummy.c | 133 +++++++++++++++++++++++++++++++++-
lib/dpif-offload-provider.h | 13 ++++
lib/dpif-offload.c | 19 +++++
lib/dpif-offload.h | 4 +-
lib/netdev-native-tnl.c | 11 ++-
lib/netdev-native-tnl.h | 5 ++
lib/netdev-provider.h | 9 ++-
lib/netdev.c | 4 +-
lib/netdev.h | 1 +
tests/dpif-netdev.at | 101 +++++++++++++++++++++++---
utilities/checkpatch_dict.txt | 1 +
12 files changed, 298 insertions(+), 21 deletions(-)
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 64531a02c0..9384edcd96 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -8374,8 +8374,9 @@ push_tnl_action(const struct dp_netdev_pmd_thread *pmd,
const struct nlattr *attr,
struct dp_packet_batch *batch)
{
- struct tx_port *tun_port;
+ const struct netdev *ingress_netdev = NULL;
const struct ovs_action_push_tnl *data;
+ struct tx_port *tun_port;
int err;
data = nl_attr_get(attr);
@@ -8385,7 +8386,20 @@ push_tnl_action(const struct dp_netdev_pmd_thread *pmd,
err = -EINVAL;
goto error;
}
- err = netdev_push_header(tun_port->port->netdev, batch, data);
+
+ if (dpif_offload_enabled() && !dp_packet_batch_is_empty(batch)) {
+ /* To avoid multiple port lookups per batch, assume that all packets
+ * in the batch originate from the same flow and therefore share the
+ * same original input port. */
+ struct tx_port *in_port = pmd_send_port_cache_lookup(
+ pmd, batch->packets[0]->md.orig_in_port);
+ if (in_port) {
+ ingress_netdev = in_port->port->netdev;
+ }
+ }
+
+ err = netdev_push_header(tun_port->port->netdev, ingress_netdev, batch,
+ data);
if (!err) {
return 0;
}
diff --git a/lib/dpif-offload-dummy.c b/lib/dpif-offload-dummy.c
index 3dd9eff14b..4c199c3aec 100644
--- a/lib/dpif-offload-dummy.c
+++ b/lib/dpif-offload-dummy.c
@@ -24,6 +24,7 @@
#include "dummy.h"
#include "id-fpool.h"
#include "netdev-provider.h"
+#include "netdev-native-tnl.h"
#include "odp-util.h"
#include "util.h"
#include "uuid.h"
@@ -613,6 +614,35 @@ dummy_offload_hw_post_process(const struct dpif_offload
*offload_,
return 0;
}
+static ovs_be16
+dummy_offload_udp_tnl_get_src_port__(const struct dp_packet *packet)
+{
+ /* Use FNV-1a hash to ensure consistent results across all platforms. The
+ * standard OVS hash functions have architecture-specific implementations
+ * (SSE4.2, ARM64 optimizations, etc.) that produce different outputs for
+ * identical inputs, making tests non-deterministic. */
+ const uint8_t *data = dp_packet_data(packet);
+ size_t len = dp_packet_size(packet);
+ uint32_t hash = 2166136261U;
+ uint32_t prime = 16777619U;
+
+ for (size_t i = 0; i < len; i++) {
+ hash ^= data[i];
+ hash *= prime;
+ }
+ return htons((uint16_t) hash);
+}
+
+static bool
+dummy_offload_udp_tnl_get_src_port(
+ const struct dpif_offload *offload OVS_UNUSED,
+ const struct netdev *ingress_netdev OVS_UNUSED,
+ const struct dp_packet *packet, ovs_be16 *src_port)
+{
+ *src_port = dummy_offload_udp_tnl_get_src_port__(packet);
+ return true;
+}
+
static bool
dummy_offload_are_all_actions_supported(const struct dpif_offload *offload_,
odp_port_t in_odp,
@@ -631,7 +661,10 @@ dummy_offload_are_all_actions_supported(const struct
dpif_offload *offload_,
* that they provide full protection when calling netdev_send() from any
* thread, via a netdev-level mutex. */
NL_ATTR_FOR_EACH (nla, left, actions, actions_len) {
- if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
+ enum ovs_action_attr action = nl_attr_type(nla);
+
+ switch (action) {
+ case OVS_ACTION_ATTR_OUTPUT: {
odp_port_t out_odp = nl_attr_get_odp_port(nla);
struct dummy_offload_port *out_port;
@@ -641,7 +674,49 @@ dummy_offload_are_all_actions_supported(const struct
dpif_offload *offload_,
netdev_get_type(out_port->pm_port.netdev))) {
return false;
}
- } else {
+ break;
+ }
+
+ case OVS_ACTION_ATTR_TUNNEL_PUSH: {
+ /* We only support UDP tunnels, i.e. VXLAN and Geneve. */
+ const struct ovs_action_push_tnl *data = nl_attr_get(nla);
+
+ if (data->tnl_type != OVS_VPORT_TYPE_VXLAN
+ && data->tnl_type != OVS_VPORT_TYPE_GENEVE) {
+ return false;
+ }
+ break;
+ }
+
+ case OVS_ACTION_ATTR_UNSPEC:
+ case OVS_ACTION_ATTR_USERSPACE:
+ case OVS_ACTION_ATTR_SET:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_SAMPLE:
+ case OVS_ACTION_ATTR_RECIRC:
+ case OVS_ACTION_ATTR_HASH:
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+ case OVS_ACTION_ATTR_POP_MPLS:
+ case OVS_ACTION_ATTR_SET_MASKED:
+ case OVS_ACTION_ATTR_CT:
+ case OVS_ACTION_ATTR_TRUNC:
+ case OVS_ACTION_ATTR_PUSH_ETH:
+ case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_CT_CLEAR:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
+ case OVS_ACTION_ATTR_METER:
+ case OVS_ACTION_ATTR_CLONE:
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
+ case OVS_ACTION_ATTR_ADD_MPLS:
+ case OVS_ACTION_ATTR_DEC_TTL:
+ case OVS_ACTION_ATTR_DROP:
+ case OVS_ACTION_ATTR_PSAMPLE:
+ case OVS_ACTION_ATTR_TUNNEL_POP:
+ case OVS_ACTION_ATTR_LB_OUTPUT:
+ case __OVS_ACTION_ATTR_MAX:
+ default:
return false;
}
}
@@ -664,8 +739,10 @@ dummy_offload_hw_process_pkt(const struct dpif_offload
*offload_,
NL_ATTR_FOR_EACH (nla, left, flow->actions, flow->actions_len) {
bool last_action = (left <= NLA_ALIGN(nla->nla_len));
+ enum ovs_action_attr action = nl_attr_type(nla);
- if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
+ switch (action) {
+ case OVS_ACTION_ATTR_OUTPUT: {
odp_port_t odp_port = nl_attr_get_odp_port(nla);
struct dummy_offload_port *port;
struct dp_packet_batch batch;
@@ -684,6 +761,55 @@ dummy_offload_hw_process_pkt(const struct dpif_offload
*offload_,
* for now we assume hash steering based on the number of queues
* configured for the dummy-netdev. */
netdev_send(port->pm_port.netdev, hash % n_txq, &batch, false);
+ break;
+ }
+ case OVS_ACTION_ATTR_TUNNEL_PUSH: {
+ const struct ovs_action_push_tnl *data = nl_attr_get(nla);
+ struct udp_header *udp;
+ struct flow ovs_flow;
+ ovs_be16 src_port;
+
+ src_port = dummy_offload_udp_tnl_get_src_port__(pkt);
+ netdev_tnl_push_udp_header(NULL, NULL, pkt, data);
+
+ flow_extract(pkt, &ovs_flow);
+ udp = dp_packet_l4(pkt);
+ ovs_assert(ovs_flow.nw_proto == IPPROTO_UDP && udp);
+
+ udp->udp_src = src_port;
+ break;
+ }
+
+ case OVS_ACTION_ATTR_UNSPEC:
+ case OVS_ACTION_ATTR_USERSPACE:
+ case OVS_ACTION_ATTR_SET:
+ case OVS_ACTION_ATTR_PUSH_VLAN:
+ case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_SAMPLE:
+ case OVS_ACTION_ATTR_RECIRC:
+ case OVS_ACTION_ATTR_HASH:
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+ case OVS_ACTION_ATTR_POP_MPLS:
+ case OVS_ACTION_ATTR_SET_MASKED:
+ case OVS_ACTION_ATTR_CT:
+ case OVS_ACTION_ATTR_TRUNC:
+ case OVS_ACTION_ATTR_PUSH_ETH:
+ case OVS_ACTION_ATTR_POP_ETH:
+ case OVS_ACTION_ATTR_CT_CLEAR:
+ case OVS_ACTION_ATTR_PUSH_NSH:
+ case OVS_ACTION_ATTR_POP_NSH:
+ case OVS_ACTION_ATTR_METER:
+ case OVS_ACTION_ATTR_CLONE:
+ case OVS_ACTION_ATTR_CHECK_PKT_LEN:
+ case OVS_ACTION_ATTR_ADD_MPLS:
+ case OVS_ACTION_ATTR_DEC_TTL:
+ case OVS_ACTION_ATTR_DROP:
+ case OVS_ACTION_ATTR_PSAMPLE:
+ case OVS_ACTION_ATTR_TUNNEL_POP:
+ case OVS_ACTION_ATTR_LB_OUTPUT:
+ case __OVS_ACTION_ATTR_MAX:
+ default:
+ OVS_NOT_REACHED();
}
}
@@ -1062,6 +1188,7 @@ dummy_pmd_thread_lifecycle(const struct dpif_offload
*dpif_offload,
.port_del = dummy_offload_port_del, \
.get_netdev = dummy_offload_get_netdev, \
.netdev_hw_post_process = dummy_offload_hw_post_process, \
+ .netdev_udp_tnl_get_src_port = dummy_offload_udp_tnl_get_src_port, \
.netdev_flow_put = dummy_flow_put, \
.netdev_flow_del = dummy_flow_del, \
.netdev_flow_stats = dummy_flow_stats, \
diff --git a/lib/dpif-offload-provider.h b/lib/dpif-offload-provider.h
index 259de2c299..e55d599519 100644
--- a/lib/dpif-offload-provider.h
+++ b/lib/dpif-offload-provider.h
@@ -279,6 +279,19 @@ struct dpif_offload_class {
unsigned pmd_id, struct dp_packet *,
void **flow_reference);
+ /* Allows the offload provider to override the default UDP tunnel source
+ * port selection. Called during tunnel encapsulation to determine the
+ * source port for UDP-based tunnels (VXLAN, Geneve, etc.).
+ *
+ * If implemented, should return true and set 'src_port' to the desired
+ * source port value. If not implemented or if default behavior is
+ * desired, should return false to use the standard source port
+ * calculation. */
+ bool (*netdev_udp_tnl_get_src_port)(const struct dpif_offload *,
+ const struct netdev *ingress_netdev,
+ const struct dp_packet *packet,
+ ovs_be16 *src_port);
+
/* Add or modify the specified flow directly in the offload datapath.
* The actual implementation may choose to handle the offload
* asynchronously by returning EINPROGRESS and invoking the supplied
diff --git a/lib/dpif-offload.c b/lib/dpif-offload.c
index cbf1f6c704..341decfdff 100644
--- a/lib/dpif-offload.c
+++ b/lib/dpif-offload.c
@@ -1465,6 +1465,25 @@ dpif_offload_netdev_hw_post_process(struct netdev
*netdev, unsigned pmd_id,
return rc;
}
+bool
+dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *ingress_netdev,
+ const struct dp_packet *packet,
+ ovs_be16 *src_port)
+{
+ const struct dpif_offload *offload;
+
+ offload = ovsrcu_get(const struct dpif_offload *,
+ &ingress_netdev->dpif_offload);
+
+ if (OVS_UNLIKELY(!offload)
+ || !offload->class->netdev_udp_tnl_get_src_port) {
+ return false;
+ }
+
+ return offload->class->netdev_udp_tnl_get_src_port(offload, ingress_netdev,
+ packet, src_port);
+}
+
void
dpif_offload_datapath_register_flow_unreference_cb(
struct dpif *dpif, dpif_offload_flow_unreference_cb *cb)
diff --git a/lib/dpif-offload.h b/lib/dpif-offload.h
index 0f66d8cd8e..6671b3147a 100644
--- a/lib/dpif-offload.h
+++ b/lib/dpif-offload.h
@@ -114,8 +114,10 @@ bool dpif_offload_netdev_same_offload(const struct netdev
*,
int dpif_offload_netdev_hw_post_process(struct netdev *, unsigned pmd_id,
struct dp_packet *,
void **flow_reference);
+bool dpif_offload_netdev_udp_tnl_get_src_port(const struct netdev *,
+ const struct dp_packet *,
+ ovs_be16 *src_port);
-
/* Callback invoked when a hardware flow offload operation (put/del) completes.
* This callback is used for asynchronous flow offload operations. When the
* offload provider cannot complete an operation synchronously (returns
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 008b452b8a..f27b8b6926 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -36,6 +36,7 @@
#include "coverage.h"
#include "csum.h"
#include "dp-packet.h"
+#include "dpif-offload.h"
#include "netdev.h"
#include "netdev-vport.h"
#include "netdev-vport-private.h"
@@ -301,6 +302,7 @@ tnl_ol_pop(struct dp_packet *packet, int off)
void
netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,
+ const struct netdev *ingress_netdev,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data)
{
@@ -312,7 +314,10 @@ netdev_tnl_push_udp_header(const struct netdev *netdev
OVS_UNUSED,
/* We may need to re-calculate the hash and this has to be done before
* modifying the packet. */
- udp_src = netdev_tnl_get_src_port(packet);
+ if (!ingress_netdev || !dpif_offload_netdev_udp_tnl_get_src_port(
+ ingress_netdev, packet, &udp_src)) {
+ udp_src = netdev_tnl_get_src_port(packet);
+ }
tnl_ol_push(packet, data);
udp = netdev_tnl_push_ip_header(packet, data->header, data->header_len,
@@ -532,6 +537,7 @@ err:
void
netdev_gre_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev OVS_UNUSED,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data)
{
@@ -695,6 +701,7 @@ err:
void
netdev_erspan_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev OVS_UNUSED,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data)
{
@@ -868,6 +875,7 @@ err:
void
netdev_gtpu_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev OVS_UNUSED,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data)
{
@@ -1001,6 +1009,7 @@ netdev_srv6_build_header(const struct netdev *netdev,
void
netdev_srv6_push_header(const struct netdev *netdev OVS_UNUSED,
+ const struct netdev *ingress_netdev OVS_UNUSED,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data)
{
diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h
index 47d6b6bbcf..ec1531d550 100644
--- a/lib/netdev-native-tnl.h
+++ b/lib/netdev-native-tnl.h
@@ -35,6 +35,7 @@ netdev_gre_build_header(const struct netdev *netdev,
void
netdev_gre_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data);
struct dp_packet *
@@ -47,6 +48,7 @@ netdev_erspan_build_header(const struct netdev *netdev,
void
netdev_erspan_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data);
struct dp_packet *
@@ -57,6 +59,7 @@ netdev_gtpu_pop_header(struct dp_packet *packet);
void
netdev_gtpu_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data);
@@ -68,6 +71,7 @@ netdev_gtpu_build_header(const struct netdev *netdev,
struct dp_packet *netdev_srv6_pop_header(struct dp_packet *);
void netdev_srv6_push_header(const struct netdev *,
+ const struct netdev *ingress_netdev,
struct dp_packet *,
const struct ovs_action_push_tnl *);
@@ -77,6 +81,7 @@ int netdev_srv6_build_header(const struct netdev *,
void
netdev_tnl_push_udp_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data);
int
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index 136d8188c2..9aedb101fb 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -336,8 +336,13 @@ struct netdev_class {
/* build_header() can not build entire header for all packets for given
* flow. Push header is called for packet to build header specific to
* a packet on actual transmit. It uses partial header build by
- * build_header() which is passed as data. */
- void (*push_header)(const struct netdev *,
+ * build_header() which is passed as data.
+ *
+ * The 'ingress_netdev' points to the original ingress netdev for the
+ * 'packet'. This variable is valid only if hardware offload is enabled;
+ * otherwise, it will be NULL. */
+ void (*push_header)(const struct netdev *netdev,
+ const struct netdev *ingress_netdev,
struct dp_packet *packet,
const struct ovs_action_push_tnl *data);
diff --git a/lib/netdev.c b/lib/netdev.c
index daa4287362..2f08468c21 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -1003,6 +1003,7 @@ int netdev_build_header(const struct netdev *netdev,
* that netdev_has_tunnel_push_pop() returns true. */
int
netdev_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev,
struct dp_packet_batch *batch,
const struct ovs_action_push_tnl *data)
{
@@ -1038,7 +1039,8 @@ netdev_push_header(const struct netdev *netdev,
}
dp_packet_ol_send_prepare(packet, 0);
}
- netdev->netdev_class->push_header(netdev, packet, data);
+ netdev->netdev_class->push_header(netdev, ingress_netdev, packet,
+ data);
pkt_metadata_init(&packet->md, data->out_port);
dp_packet_batch_refill(batch, packet, i);
diff --git a/lib/netdev.h b/lib/netdev.h
index 40f1621eca..8aab8bc062 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -259,6 +259,7 @@ int netdev_build_header(const struct netdev *, struct
ovs_action_push_tnl *data,
const struct netdev_tnl_build_header_params *params);
int netdev_push_header(const struct netdev *netdev,
+ const struct netdev *ingress_netdev,
struct dp_packet_batch *,
const struct ovs_action_push_tnl *data);
void netdev_pop_header(struct netdev *netdev, struct dp_packet_batch *);
diff --git a/tests/dpif-netdev.at b/tests/dpif-netdev.at
index 0480730cab..5b6c961f90 100644
--- a/tests/dpif-netdev.at
+++ b/tests/dpif-netdev.at
@@ -77,6 +77,17 @@ strip_metadata () {
]
m4_divert_pop([PREPARE_TESTS])
+m4_define([CHECK_FWD_PACKET],
+ [m4_if([$3], [], [], [AT_CHECK([ovs-vsctl set interface $1
options:$3=true])])
+ AT_CHECK([ovs-appctl netdev-dummy/receive $1 $(cat $4)])
+ m4_if([$5], [none], [], [
+ AT_CHECK([awk 1 $5 > expout]) dnl Also normalizes the line endings.
+ AT_CHECK([ovs-pcap $2.pcap > $2.pcap.txt 2>&1])
+ AT_CHECK([tail -n 1 $2.pcap.txt], [0], [expout])
+ ])
+ m4_if([$3], [], [], [AT_CHECK([ovs-vsctl remove interface $1 options $3])])
+])
+
AT_SETUP([dpif-netdev - netdev-dummy/receive])
# Create br0 with interfaces p0
OVS_VSWITCHD_START([add-port br0 p1 -- set interface p1 type=dummy
ofport_request=1 -- ])
@@ -761,6 +772,85 @@ rx_offload_pipe_abort:1
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([dpif-netdev - full hw offload - tunnel entropy - dummy-pmd])
+OVS_VSWITCHD_START(
+ [add-br br1 -- set bridge br1 datapath-type=dummy -- \
+ add-port br1 p1 -- \
+ set Interface p1 type=dummy-pmd -- \
+ add-br br2 -- set bridge br2 datapath-type=dummy -- \
+ set bridge br2 other-config:hwaddr=aa:66:aa:66:00:00 -- \
+ add-port br2 p2 -- \
+ set Interface p2 type=dummy-pmd], [], [], [--dummy-numa 0])
+
+AT_CHECK([ovs-vsctl set Open_vSwitch . other_config:hw-offload=true])
+OVS_WAIT_UNTIL([grep "Flow HW offload is enabled" ovs-vswitchd.log])
+
+AT_CHECK([ovs-vsctl add-port br1 t1 \
+ -- set Interface t1 type=vxlan \
+ options:remote_ip=1.1.2.92 options:key=123 \
+ options:csum=false ofport_request=11], [0])
+AT_CHECK([ovs-ofctl add-flow br1
in_port=p1,actions=mod_nw_dst:192.168.1.1,output:11])
+
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br2 1.1.2.88/24], [0], [OK
+])
+AT_CHECK([ovs-appctl tnl/neigh/set br2 1.1.2.92 aa:66:aa:66:00:01], [0], [OK
+])
+AT_CHECK([ovs-appctl tnl/egress_port_range 57363 57363], [0], [OK
+])
+AT_CHECK([ovs-appctl vlog/set dpif:file:dbg dpif_netdev:file:dbg \
+ dpif_offload_dummy:file:dbg netdev_dummy:file:dbg])
+
+AT_CHECK([ovs-vsctl set Interface p2 options:tx_pcap=p2.pcap])
+
+dnl ICMPv6.
+AT_DATA([ndp_frame], m4_join([],
+dnl p = Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01')
+[3333ff00000104bf1bd82d2d86dd],
+dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1')
+[6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001],
+dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1')
+[87006e2600000000fe800000000000000000000000000001],
+dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84')
+[01018abf7e2f0584]
+))
+
+AT_DATA([ndp_expected], m4_join([],
+dnl p = Ether(src='aa:66:aa:66:00:00', dst='aa:66:aa:66:00:01')
+[aa66aa660001aa66aa6600000800],
+dnl p /= IP(src='1.1.2.88', dst='1.1.2.92', id=0, flags='DF')
+[4500007a00004000401133be010102580101025c],
+dnl p /= UDP(sport=43938, chksum=0)
+[aba212b500660000],
+dnl p /= VXLAN(vni=123, flags='Instance')
+[0800000000007b00],
+dnl p /= Ether(src='04:bf:1b:d8:2d:2d', dst='33:33:ff:00:00:01')
+[3333ff00000104bf1bd82d2d86dd],
+dnl p /= IPv6(src='fe80::2', dst='ff02::1:ff00:1')
+[6000000000203afffe800000000000000000000000000002ff0200000000000000000001ff000001],
+dnl p /= ICMPv6ND_NS(type=135, tgt='fe80::1')
+[87006e2600000000fe800000000000000000000000000001],
+dnl p /= ICMPv6NDOptSrcLLAddr(lladdr='8a:bf:7e:2f:05:84')
+[01018abf7e2f0584]
+))
+
+# Sent two packets, second should be handled in hardware.
+CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_expected])
+CHECK_FWD_PACKET(p1, p2, , [ndp_frame], [ndp_expected])
+
+# Check if we do not hit partial hw offload.
+AT_CHECK(
+ [ovs-appctl --format json dpif/offload/show \
+ | sed 's/.*"p1":{\([[^}]]*\)}.*/\1/; s/,/\n/g; s/"//g' \
+ | sed -n '/^rx_offload_/p' | sort], [0], [dnl
+rx_offload_full:1
+rx_offload_miss:1
+rx_offload_partial:0
+rx_offload_pipe_abort:0
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([dpif-netdev - check dpctl/add-flow in_port exact match])
OVS_VSWITCHD_START(
[add-port br0 p1 \
@@ -877,17 +967,6 @@ AT_CHECK([test `ovs-vsctl get Interface p2
statistics:tx_q0_packets` -gt 0 -a dn
OVS_VSWITCHD_STOP
AT_CLEANUP
-m4_define([CHECK_FWD_PACKET],
- [m4_if([$3], [], [], [AT_CHECK([ovs-vsctl set interface $1
options:$3=true])])
- AT_CHECK([ovs-appctl netdev-dummy/receive $1 $(cat $4)])
- m4_if([$5], [none], [], [
- AT_CHECK([awk 1 $5 > expout]) dnl Also normalizes the line endings.
- AT_CHECK([ovs-pcap $2.pcap > $2.pcap.txt 2>&1])
- AT_CHECK([tail -n 1 $2.pcap.txt], [0], [expout])
- ])
- m4_if([$3], [], [], [AT_CHECK([ovs-vsctl remove interface $1 options $3])])
-])
-
dnl CHECK_IP_CHECKSUMS rx_port tx_port good_pkt bad_pkt good_exp bad_exp
dnl
dnl Test combinations of Rx IP checksum flags for a good or bad packet
diff --git a/utilities/checkpatch_dict.txt b/utilities/checkpatch_dict.txt
index 0972cba603..35d2fd95bb 100644
--- a/utilities/checkpatch_dict.txt
+++ b/utilities/checkpatch_dict.txt
@@ -142,6 +142,7 @@ lldp
llvm
localnet
lockless
+lookups
loopback
malloc
mbps
--
2.52.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev