[ovs-dev] [PATCH v3] bundle: add symmetric_l3 hash method for multipath

Martin Xu martinxu9.ovs at gmail.com
Tue Oct 2 16:40:09 UTC 2018


Add a symmetric_l3 hash method that uses both network destination
address and network source address.

VMware-BZ: #2112940

Signed-off-by: Martin Xu <martinxu9.ovs at gmail.com>
CC: Ben Pfaff <blp at ovn.org>
---
v1->v2: add eth_type as part of the hash fields; 
        added unit tests in multipath, bundle, oss-fuzz, ovs-ofctl
v2->v3: add to the NEWS
        
 NEWS                                 |   1 +
 include/openflow/nicira-ext.h        |   4 +-
 lib/bundle.c                         |   2 +
 lib/flow.c                           |  47 +++++-
 lib/flow.h                           |   1 +
 lib/multipath.c                      |   2 +
 tests/bundle.at                      | 179 +++++++++++++++++++++++
 tests/multipath.at                   | 271 +++++++++++++++++++++++++++++++++++
 tests/oss-fuzz/flow_extract_target.c |   1 +
 tests/ovs-ofctl.at                   |  12 ++
 utilities/ovs-ofctl.8.in             |   2 +
 11 files changed, 520 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 8b6f3ce..2e478b9 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,7 @@ Post-v2.10.0
    - DPDK:
      * Add option for simple round-robin based Rxq to PMD assignment.
        It can be set with pmd-rxq-assign.
+   - Add 'symmetric_l3' hash function.
 
 v2.10.0 - 18 Aug 2018
 ---------------------
diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index 1f5cbbf..dc12101 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -109,8 +109,10 @@ enum nx_hash_fields {
     NX_HASH_FIELDS_NW_SRC,
 
     /* Network destination address (NXM_OF_IP_DST) only. */
-    NX_HASH_FIELDS_NW_DST
+    NX_HASH_FIELDS_NW_DST,
 
+    /* Both network destination and source destination addresses. */
+    NX_HASH_FIELDS_SYMMETRIC_L3
 };
 
 /* NXT_PACKET_IN (analogous to OFPT_PACKET_IN).
diff --git a/lib/bundle.c b/lib/bundle.c
index edf6676..558e890 100644
--- a/lib/bundle.c
+++ b/lib/bundle.c
@@ -198,6 +198,8 @@ bundle_parse__(const char *s, const struct ofputil_port_map *port_map,
         bundle->fields = NX_HASH_FIELDS_NW_SRC;
     } else if (!strcasecmp(fields, "nw_dst")) {
         bundle->fields = NX_HASH_FIELDS_NW_DST;
+    } else if (!strcasecmp(fields, "symmetric_l3")) {
+        bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L3;
     } else {
         return xasprintf("%s: unknown fields `%s'", s, fields);
     }
diff --git a/lib/flow.c b/lib/flow.c
index bffee70..79e4627 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -2307,6 +2307,37 @@ flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis,
     return hash_finish(hash, basis);
 }
 
+/* Hashes 'flow' based on its nw_dst and nw_src for multipath. */
+uint32_t
+flow_hash_symmetric_l3(const struct flow *flow, uint32_t basis)
+{
+    struct {
+        union {
+            ovs_be32 ipv4_addr;
+            struct in6_addr ipv6_addr;
+        };
+        ovs_be16 eth_type;
+    } fields;
+
+    int i;
+
+    memset(&fields, 0, sizeof fields);
+    fields.eth_type = flow->dl_type;
+
+    if (fields.eth_type == htons(ETH_TYPE_IP)) {
+        fields.ipv4_addr = flow->nw_src ^ flow->nw_dst;
+    } else if (fields.eth_type == htons(ETH_TYPE_IPV6)) {
+        const uint8_t *a = &flow->ipv6_src.s6_addr[0];
+        const uint8_t *b = &flow->ipv6_dst.s6_addr[0];
+        uint8_t *ipv6_addr = &fields.ipv6_addr.s6_addr[0];
+
+        for (i = 0; i < 16; i++) {
+            ipv6_addr[i] = a[i] ^ b[i];
+        }
+    }
+    return jhash_bytes(&fields, sizeof fields, basis);
+}
+
 /* Initialize a flow with random fields that matter for nx_hash_fields. */
 void
 flow_random_hash_fields(struct flow *flow)
@@ -2422,6 +2453,16 @@ flow_mask_hash_fields(const struct flow *flow, struct flow_wildcards *wc,
         }
         break;
 
+    case NX_HASH_FIELDS_SYMMETRIC_L3:
+        if (flow->dl_type == htons(ETH_TYPE_IP)) {
+            memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
+            memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
+        } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+            memset(&wc->masks.ipv6_src, 0xff, sizeof wc->masks.ipv6_src);
+            memset(&wc->masks.ipv6_dst, 0xff, sizeof wc->masks.ipv6_dst);
+        }
+        break;
+
     default:
         OVS_NOT_REACHED();
     }
@@ -2464,6 +2505,8 @@ flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields,
             return basis;
         }
 
+    case NX_HASH_FIELDS_SYMMETRIC_L3:
+        return flow_hash_symmetric_l3(flow, basis);
     }
 
     OVS_NOT_REACHED();
@@ -2480,6 +2523,7 @@ flow_hash_fields_to_str(enum nx_hash_fields fields)
     case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP: return "symmetric_l3l4+udp";
     case NX_HASH_FIELDS_NW_SRC: return "nw_src";
     case NX_HASH_FIELDS_NW_DST: return "nw_dst";
+    case NX_HASH_FIELDS_SYMMETRIC_L3: return "symmetric_l3";
     default: return "<unknown>";
     }
 }
@@ -2493,7 +2537,8 @@ flow_hash_fields_valid(enum nx_hash_fields fields)
         || fields == NX_HASH_FIELDS_SYMMETRIC_L3L4
         || fields == NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP
         || fields == NX_HASH_FIELDS_NW_SRC
-        || fields == NX_HASH_FIELDS_NW_DST;
+        || fields == NX_HASH_FIELDS_NW_DST
+        || fields == NX_HASH_FIELDS_SYMMETRIC_L3;
 }
 
 /* Returns a hash value for the bits of 'flow' that are active based on
diff --git a/lib/flow.h b/lib/flow.h
index d03f1ba..1f6c1d6 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -241,6 +241,7 @@ uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis);
 uint32_t flow_hash_symmetric_l2(const struct flow *flow, uint32_t basis);
 uint32_t flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis,
                          bool inc_udp_ports );
+uint32_t flow_hash_symmetric_l3(const struct flow *flow, uint32_t basis);
 
 /* Initialize a flow with random fields that matter for nx_hash_fields. */
 void flow_random_hash_fields(struct flow *);
diff --git a/lib/multipath.c b/lib/multipath.c
index 659c0bd..e6ba4c6 100644
--- a/lib/multipath.c
+++ b/lib/multipath.c
@@ -171,6 +171,8 @@ multipath_parse__(struct ofpact_multipath *mp, const char *s_, char *s)
         mp->fields = NX_HASH_FIELDS_NW_SRC;
     } else if (!strcasecmp(fields, "nw_dst")) {
         mp->fields = NX_HASH_FIELDS_NW_DST;
+    } else if (!strcasecmp(fields, "symmetric_l3")) {
+        mp->fields = NX_HASH_FIELDS_SYMMETRIC_L3;
     } else {
         return xasprintf("%s: unknown fields `%s'", s_, fields);
     }
diff --git a/tests/bundle.at b/tests/bundle.at
index 40dfbea..0a4eadc 100644
--- a/tests/bundle.at
+++ b/tests/bundle.at
@@ -273,3 +273,182 @@ AT_CHECK([grep Final stdout], [0],
 ])
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([hrw bundle symmetric_l3 link selection])
+AT_KEYWORDS([bundle_action])
+AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],slaves:1,2,3,4,5']],
+  [0], [ignore])
+# 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+# 110000: disruption=0.50 (perfect=0.50) 0.50 0.50 0.00 0.00 0.00 0.00
+# 010000: disruption=0.50 (perfect=0.50) 0.00 1.00 0.00 0.00 0.00 0.00
+# 011000: disruption=0.50 (perfect=0.50) 0.00 0.50 0.50 0.00 0.00 0.00
+# 111000: disruption=0.33 (perfect=0.33) 0.33 0.33 0.34 0.00 0.00 0.00
+# 101000: disruption=0.33 (perfect=0.33) 0.50 0.00 0.50 0.00 0.00 0.00
+# 001000: disruption=0.50 (perfect=0.50) 0.00 0.00 1.00 0.00 0.00 0.00
+# 001100: disruption=0.50 (perfect=0.50) 0.00 0.00 0.50 0.50 0.00 0.00
+# 101100: disruption=0.33 (perfect=0.33) 0.33 0.00 0.34 0.33 0.00 0.00
+# 111100: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.25 0.00 0.00
+# 011100: disruption=0.25 (perfect=0.25) 0.00 0.33 0.33 0.33 0.00 0.00
+# 010100: disruption=0.33 (perfect=0.33) 0.00 0.50 0.00 0.50 0.00 0.00
+# 110100: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.34 0.00 0.00
+# 100100: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.50 0.00 0.00
+# 000100: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 1.00 0.00 0.00
+# 000110: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.50 0.50 0.00
+# 100110: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.33 0.33 0.00
+# 110110: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.25 0.25 0.00
+# 010110: disruption=0.25 (perfect=0.25) 0.00 0.34 0.00 0.33 0.33 0.00
+# 011110: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.25 0.25 0.00
+# 111110: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.20 0.20 0.00
+# 101110: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.25 0.25 0.00
+# 001110: disruption=0.25 (perfect=0.25) 0.00 0.00 0.34 0.33 0.33 0.00
+# 001010: disruption=0.33 (perfect=0.33) 0.00 0.00 0.50 0.00 0.50 0.00
+# 101010: disruption=0.33 (perfect=0.33) 0.33 0.00 0.34 0.00 0.33 0.00
+# 111010: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.00 0.25 0.00
+# 011010: disruption=0.25 (perfect=0.25) 0.00 0.33 0.34 0.00 0.33 0.00
+# 010010: disruption=0.34 (perfect=0.33) 0.00 0.50 0.00 0.00 0.50 0.00
+# 110010: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.00 0.33 0.00
+# 100010: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.00 0.50 0.00
+# 000010: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 1.00 0.00
+# 000011: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 0.50 0.50
+# 100011: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.00 0.33 0.33
+# 110011: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.00 0.25 0.25
+# 010011: disruption=0.25 (perfect=0.25) 0.00 0.33 0.00 0.00 0.33 0.33
+# 011011: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.00 0.25 0.25
+# 111011: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.00 0.20 0.20
+# 101011: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.00 0.25 0.25
+# 001011: disruption=0.25 (perfect=0.25) 0.00 0.00 0.34 0.00 0.33 0.33
+# 001111: disruption=0.25 (perfect=0.25) 0.00 0.00 0.25 0.25 0.25 0.25
+# 101111: disruption=0.20 (perfect=0.20) 0.20 0.00 0.20 0.20 0.20 0.20
+# 111111: disruption=0.17 (perfect=0.17) 0.17 0.17 0.17 0.17 0.17 0.17
+# 011111: disruption=0.17 (perfect=0.17) 0.00 0.20 0.20 0.20 0.20 0.20
+# 010111: disruption=0.20 (perfect=0.20) 0.00 0.25 0.00 0.25 0.25 0.25
+# 110111: disruption=0.20 (perfect=0.20) 0.20 0.20 0.00 0.20 0.20 0.20
+# 100111: disruption=0.20 (perfect=0.20) 0.25 0.00 0.00 0.25 0.25 0.25
+# 000111: disruption=0.25 (perfect=0.25) 0.00 0.00 0.00 0.33 0.33 0.33
+# 000101: disruption=0.33 (perfect=0.33) 0.00 0.00 0.00 0.50 0.00 0.50
+# 100101: disruption=0.33 (perfect=0.33) 0.33 0.00 0.00 0.33 0.00 0.33
+# 110101: disruption=0.25 (perfect=0.25) 0.25 0.25 0.00 0.25 0.00 0.25
+# 010101: disruption=0.25 (perfect=0.25) 0.00 0.33 0.00 0.33 0.00 0.33
+# 011101: disruption=0.25 (perfect=0.25) 0.00 0.25 0.25 0.25 0.00 0.25
+# 111101: disruption=0.20 (perfect=0.20) 0.20 0.20 0.20 0.20 0.00 0.20
+# 101101: disruption=0.20 (perfect=0.20) 0.25 0.00 0.25 0.25 0.00 0.25
+# 001101: disruption=0.25 (perfect=0.25) 0.00 0.00 0.33 0.33 0.00 0.33
+# 001001: disruption=0.33 (perfect=0.33) 0.00 0.00 0.50 0.00 0.00 0.50
+# 101001: disruption=0.33 (perfect=0.33) 0.33 0.00 0.33 0.00 0.00 0.33
+# 111001: disruption=0.25 (perfect=0.25) 0.25 0.25 0.25 0.00 0.00 0.25
+# 011001: disruption=0.25 (perfect=0.25) 0.00 0.33 0.34 0.00 0.00 0.33
+# 010001: disruption=0.34 (perfect=0.33) 0.00 0.50 0.00 0.00 0.00 0.50
+# 110001: disruption=0.33 (perfect=0.33) 0.33 0.33 0.00 0.00 0.00 0.34
+# 100001: disruption=0.33 (perfect=0.33) 0.50 0.00 0.00 0.00 0.00 0.50
+# 000001: disruption=0.50 (perfect=0.50) 0.00 0.00 0.00 0.00 0.00 1.00
+# 000000: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 0.00
+# 100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+AT_CLEANUP
+
+AT_SETUP([active_backup bundle symmetric_l3 link selection])
+AT_KEYWORDS([bundle_action])
+AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,active_backup,ofport,NXM_NX_REG0[],slaves:1,2,3,4,5,6']],
+  [0],
+[100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+110000: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+010000: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+011000: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+111000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+101000: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+001000: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00
+001100: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00
+101100: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+111100: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+011100: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+010100: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+110100: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+100100: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+000100: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 1.00 0.00 0.00
+000110: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 1.00 0.00 0.00
+100110: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+110110: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+010110: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+011110: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+111110: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+101110: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+001110: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00
+001010: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00
+101010: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+111010: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+011010: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+010010: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+110010: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+100010: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+000010: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 1.00 0.00
+000011: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 0.00 1.00 0.00
+100011: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+110011: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+010011: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+011011: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+111011: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+101011: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+001011: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00
+001111: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00
+101111: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+111111: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+011111: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+010111: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+110111: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+100111: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+000111: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 1.00 0.00 0.00
+000101: disruption=0.00 (perfect=0.00) 0.00 0.00 0.00 1.00 0.00 0.00
+100101: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+110101: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+010101: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+011101: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+111101: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+101101: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+001101: disruption=1.00 (perfect=1.00) 0.00 0.00 1.00 0.00 0.00 0.00
+001001: disruption=0.00 (perfect=0.00) 0.00 0.00 1.00 0.00 0.00 0.00
+101001: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+111001: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+011001: disruption=1.00 (perfect=1.00) 0.00 1.00 0.00 0.00 0.00 0.00
+010001: disruption=0.00 (perfect=0.00) 0.00 1.00 0.00 0.00 0.00 0.00
+110001: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+100001: disruption=0.00 (perfect=0.00) 1.00 0.00 0.00 0.00 0.00 0.00
+000001: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 1.00
+000000: disruption=1.00 (perfect=1.00) 0.00 0.00 0.00 0.00 0.00 0.00
+100000: disruption=1.00 (perfect=1.00) 1.00 0.00 0.00 0.00 0.00 0.00
+])
+AT_CLEANUP
+
+AT_SETUP([hrw bundle symmetric_l3 single link selection])
+AT_KEYWORDS([bundle_action])
+AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],slaves:1']],
+  [0], [ignore])
+# 1: disruption=1.00 (perfect=1.00) 1.00
+# 0: disruption=1.00 (perfect=1.00) 0.00
+# 1: disruption=1.00 (perfect=1.00) 1.00
+AT_CLEANUP
+
+AT_SETUP([hrw bundle symmetric_l3 single link selection])
+AT_KEYWORDS([bundle_action])
+AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],slaves:1']],
+  [0], [ignore])
+# 1: disruption=1.00 (perfect=1.00) 1.00
+# 0: disruption=1.00 (perfect=1.00) 0.00
+# 1: disruption=1.00 (perfect=1.00) 1.00
+AT_CLEANUP
+
+AT_SETUP([hrw bundle symmetric_l3 no link selection])
+AT_KEYWORDS([bundle_action])
+AT_CHECK([[ovstest test-bundle 'symmetric_l3,60,hrw,ofport,NXM_NX_REG0[],slaves:']],
+  [0], [ignore])
+AT_CLEANUP
+#: disruption=0.00 (perfect=0.00)
+#: disruption=0.00 (perfect=0.00)
+
+AT_SETUP([bundle symmetric_l3 action with many ports])
+AT_KEYWORDS([bundle_action])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl add-flow br0 'actions=set_field:0x1->metadata,set_field:0x2->metadata,set_field:0x3->metadata,set_field:0x4->metadata,bundle(symmetric_l3,0,hrw,ofport,slaves:[[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40]])'])
+AT_CHECK([ovs-ofctl dump-flows br0 --no-stats], [0], [dnl
+ actions=load:0x1->OXM_OF_METADATA[[]],load:0x2->OXM_OF_METADATA[[]],load:0x3->OXM_OF_METADATA[[]],load:0x4->OXM_OF_METADATA[[]],bundle(symmetric_l3,0,hrw,ofport,slaves:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40)
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/tests/multipath.at b/tests/multipath.at
index b5a3099..a7c880b 100644
--- a/tests/multipath.at
+++ b/tests/multipath.at
@@ -309,3 +309,274 @@ AT_CHECK([ovs-ofctl parse-flow 'actions=multipath(eth_src,50,modulo_n,1024,0,NXM
 ])
 AT_CLEANUP
 
+AT_SETUP([modulo_n multipath symmetric_l3 link selection])
+AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,modulo_n,1,0,NXM_NX_REG0[]']],
+  [0], [ignore])
+# 1 ->  2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000
+# 2 ->  3: disruption=0.66 (perfect=0.33); stddev/expected=0.0023
+# 3 ->  4: disruption=0.75 (perfect=0.25); stddev/expected=0.0061
+# 4 ->  5: disruption=0.80 (perfect=0.20); stddev/expected=0.0082
+# 5 ->  6: disruption=0.83 (perfect=0.17); stddev/expected=0.0083
+# 6 ->  7: disruption=0.86 (perfect=0.14); stddev/expected=0.0061
+# 7 ->  8: disruption=0.88 (perfect=0.12); stddev/expected=0.0103
+# 8 ->  9: disruption=0.89 (perfect=0.11); stddev/expected=0.0129
+# 9 -> 10: disruption=0.90 (perfect=0.10); stddev/expected=0.0091
+#10 -> 11: disruption=0.91 (perfect=0.09); stddev/expected=0.0114
+#11 -> 12: disruption=0.91 (perfect=0.08); stddev/expected=0.0073
+#12 -> 13: disruption=0.92 (perfect=0.08); stddev/expected=0.0165
+#13 -> 14: disruption=0.93 (perfect=0.07); stddev/expected=0.0149
+#14 -> 15: disruption=0.93 (perfect=0.07); stddev/expected=0.0127
+#15 -> 16: disruption=0.94 (perfect=0.06); stddev/expected=0.0142
+#16 -> 17: disruption=0.94 (perfect=0.06); stddev/expected=0.0098
+#17 -> 18: disruption=0.94 (perfect=0.06); stddev/expected=0.0159
+#18 -> 19: disruption=0.95 (perfect=0.05); stddev/expected=0.0121
+#19 -> 20: disruption=0.95 (perfect=0.05); stddev/expected=0.0195
+#20 -> 21: disruption=0.95 (perfect=0.05); stddev/expected=0.0120
+#21 -> 22: disruption=0.95 (perfect=0.05); stddev/expected=0.0181
+#22 -> 23: disruption=0.96 (perfect=0.04); stddev/expected=0.0222
+#23 -> 24: disruption=0.96 (perfect=0.04); stddev/expected=0.0164
+#24 -> 25: disruption=0.96 (perfect=0.04); stddev/expected=0.0146
+#25 -> 26: disruption=0.96 (perfect=0.04); stddev/expected=0.0175
+#26 -> 27: disruption=0.96 (perfect=0.04); stddev/expected=0.0231
+#27 -> 28: disruption=0.96 (perfect=0.04); stddev/expected=0.0172
+#28 -> 29: disruption=0.97 (perfect=0.03); stddev/expected=0.0211
+#29 -> 30: disruption=0.97 (perfect=0.03); stddev/expected=0.0213
+#30 -> 31: disruption=0.97 (perfect=0.03); stddev/expected=0.0253
+#31 -> 32: disruption=0.97 (perfect=0.03); stddev/expected=0.0208
+#32 -> 33: disruption=0.97 (perfect=0.03); stddev/expected=0.0223
+#33 -> 34: disruption=0.97 (perfect=0.03); stddev/expected=0.0215
+#34 -> 35: disruption=0.97 (perfect=0.03); stddev/expected=0.0201
+#35 -> 36: disruption=0.97 (perfect=0.03); stddev/expected=0.0220
+#36 -> 37: disruption=0.97 (perfect=0.03); stddev/expected=0.0221
+#37 -> 38: disruption=0.97 (perfect=0.03); stddev/expected=0.0201
+#38 -> 39: disruption=0.97 (perfect=0.03); stddev/expected=0.0215
+#39 -> 40: disruption=0.97 (perfect=0.03); stddev/expected=0.0271
+#40 -> 41: disruption=0.98 (perfect=0.02); stddev/expected=0.0272
+#41 -> 42: disruption=0.98 (perfect=0.02); stddev/expected=0.0208
+#42 -> 43: disruption=0.98 (perfect=0.02); stddev/expected=0.0226
+#43 -> 44: disruption=0.98 (perfect=0.02); stddev/expected=0.0264
+#44 -> 45: disruption=0.98 (perfect=0.02); stddev/expected=0.0233
+#45 -> 46: disruption=0.98 (perfect=0.02); stddev/expected=0.0285
+#46 -> 47: disruption=0.98 (perfect=0.02); stddev/expected=0.0246
+#47 -> 48: disruption=0.98 (perfect=0.02); stddev/expected=0.0282
+#48 -> 49: disruption=0.98 (perfect=0.02); stddev/expected=0.0233
+#49 -> 50: disruption=0.98 (perfect=0.02); stddev/expected=0.0197
+#50 -> 51: disruption=0.98 (perfect=0.02); stddev/expected=0.0317
+#51 -> 52: disruption=0.98 (perfect=0.02); stddev/expected=0.0283
+#52 -> 53: disruption=0.98 (perfect=0.02); stddev/expected=0.0282
+#53 -> 54: disruption=0.98 (perfect=0.02); stddev/expected=0.0273
+#54 -> 55: disruption=0.98 (perfect=0.02); stddev/expected=0.0283
+#55 -> 56: disruption=0.98 (perfect=0.02); stddev/expected=0.0288
+#56 -> 57: disruption=0.98 (perfect=0.02); stddev/expected=0.0263
+#57 -> 58: disruption=0.98 (perfect=0.02); stddev/expected=0.0339
+#58 -> 59: disruption=0.98 (perfect=0.02); stddev/expected=0.0262
+#59 -> 60: disruption=0.98 (perfect=0.02); stddev/expected=0.0309
+#60 -> 61: disruption=0.98 (perfect=0.02); stddev/expected=0.0285
+#61 -> 62: disruption=0.98 (perfect=0.02); stddev/expected=0.0288
+#62 -> 63: disruption=0.98 (perfect=0.02); stddev/expected=0.0298
+#63 -> 64: disruption=0.98 (perfect=0.02); stddev/expected=0.0277
+AT_CLEANUP
+
+AT_SETUP([hash_threshold multipath symmetric_l3 link selection])
+AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,hash_threshold,1,0,NXM_NX_REG0[]']],
+  [0], [ignore])
+# 1 ->  2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000
+# 2 ->  3: disruption=0.50 (perfect=0.33); stddev/expected=0.0056
+# 3 ->  4: disruption=0.50 (perfect=0.25); stddev/expected=0.0050
+# 4 ->  5: disruption=0.50 (perfect=0.20); stddev/expected=0.0074
+# 5 ->  6: disruption=0.50 (perfect=0.17); stddev/expected=0.0031
+# 6 ->  7: disruption=0.50 (perfect=0.14); stddev/expected=0.0078
+# 7 ->  8: disruption=0.50 (perfect=0.12); stddev/expected=0.0085
+# 8 ->  9: disruption=0.50 (perfect=0.11); stddev/expected=0.0093
+# 9 -> 10: disruption=0.50 (perfect=0.10); stddev/expected=0.0083
+#10 -> 11: disruption=0.51 (perfect=0.09); stddev/expected=0.0110
+#11 -> 12: disruption=0.50 (perfect=0.08); stddev/expected=0.0124
+#12 -> 13: disruption=0.50 (perfect=0.08); stddev/expected=0.0143
+#13 -> 14: disruption=0.50 (perfect=0.07); stddev/expected=0.0148
+#14 -> 15: disruption=0.50 (perfect=0.07); stddev/expected=0.0099
+#15 -> 16: disruption=0.50 (perfect=0.06); stddev/expected=0.0166
+#16 -> 17: disruption=0.50 (perfect=0.06); stddev/expected=0.0099
+#17 -> 18: disruption=0.50 (perfect=0.06); stddev/expected=0.0194
+#18 -> 19: disruption=0.50 (perfect=0.05); stddev/expected=0.0169
+#19 -> 20: disruption=0.50 (perfect=0.05); stddev/expected=0.0169
+#20 -> 21: disruption=0.50 (perfect=0.05); stddev/expected=0.0185
+#21 -> 22: disruption=0.50 (perfect=0.05); stddev/expected=0.0160
+#22 -> 23: disruption=0.50 (perfect=0.04); stddev/expected=0.0236
+#23 -> 24: disruption=0.50 (perfect=0.04); stddev/expected=0.0147
+#24 -> 25: disruption=0.50 (perfect=0.04); stddev/expected=0.0195
+#25 -> 26: disruption=0.50 (perfect=0.04); stddev/expected=0.0199
+#26 -> 27: disruption=0.50 (perfect=0.04); stddev/expected=0.0227
+#27 -> 28: disruption=0.50 (perfect=0.04); stddev/expected=0.0198
+#28 -> 29: disruption=0.50 (perfect=0.03); stddev/expected=0.0216
+#29 -> 30: disruption=0.50 (perfect=0.03); stddev/expected=0.0233
+#30 -> 31: disruption=0.50 (perfect=0.03); stddev/expected=0.0266
+#31 -> 32: disruption=0.51 (perfect=0.03); stddev/expected=0.0238
+#32 -> 33: disruption=0.50 (perfect=0.03); stddev/expected=0.0194
+#33 -> 34: disruption=0.50 (perfect=0.03); stddev/expected=0.0173
+#34 -> 35: disruption=0.50 (perfect=0.03); stddev/expected=0.0223
+#35 -> 36: disruption=0.50 (perfect=0.03); stddev/expected=0.0220
+#36 -> 37: disruption=0.50 (perfect=0.03); stddev/expected=0.0237
+#37 -> 38: disruption=0.50 (perfect=0.03); stddev/expected=0.0237
+#38 -> 39: disruption=0.50 (perfect=0.03); stddev/expected=0.0251
+#39 -> 40: disruption=0.50 (perfect=0.03); stddev/expected=0.0212
+#40 -> 41: disruption=0.50 (perfect=0.02); stddev/expected=0.0267
+#41 -> 42: disruption=0.50 (perfect=0.02); stddev/expected=0.0242
+#42 -> 43: disruption=0.50 (perfect=0.02); stddev/expected=0.0222
+#43 -> 44: disruption=0.50 (perfect=0.02); stddev/expected=0.0244
+#44 -> 45: disruption=0.50 (perfect=0.02); stddev/expected=0.0231
+#45 -> 46: disruption=0.50 (perfect=0.02); stddev/expected=0.0299
+#46 -> 47: disruption=0.50 (perfect=0.02); stddev/expected=0.0263
+#47 -> 48: disruption=0.50 (perfect=0.02); stddev/expected=0.0307
+#48 -> 49: disruption=0.50 (perfect=0.02); stddev/expected=0.0253
+#49 -> 50: disruption=0.50 (perfect=0.02); stddev/expected=0.0228
+#50 -> 51: disruption=0.50 (perfect=0.02); stddev/expected=0.0273
+#51 -> 52: disruption=0.50 (perfect=0.02); stddev/expected=0.0243
+#52 -> 53: disruption=0.50 (perfect=0.02); stddev/expected=0.0268
+#53 -> 54: disruption=0.50 (perfect=0.02); stddev/expected=0.0251
+#54 -> 55: disruption=0.50 (perfect=0.02); stddev/expected=0.0297
+#55 -> 56: disruption=0.50 (perfect=0.02); stddev/expected=0.0287
+#56 -> 57: disruption=0.50 (perfect=0.02); stddev/expected=0.0299
+#57 -> 58: disruption=0.50 (perfect=0.02); stddev/expected=0.0272
+#58 -> 59: disruption=0.50 (perfect=0.02); stddev/expected=0.0295
+#59 -> 60: disruption=0.50 (perfect=0.02); stddev/expected=0.0312
+#60 -> 61: disruption=0.50 (perfect=0.02); stddev/expected=0.0361
+#61 -> 62: disruption=0.50 (perfect=0.02); stddev/expected=0.0308
+#62 -> 63: disruption=0.50 (perfect=0.02); stddev/expected=0.0283
+#63 -> 64: disruption=0.50 (perfect=0.02); stddev/expected=0.0325
+AT_CLEANUP
+
+AT_SETUP([hrw multipath symmetric_l3 link selection])
+AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,hrw,1,0,NXM_NX_REG0[]']],
+  [0], [ignore])
+# 1 ->  2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000
+# 2 ->  3: disruption=0.33 (perfect=0.33); stddev/expected=0.0033
+# 3 ->  4: disruption=0.25 (perfect=0.25); stddev/expected=0.0076
+# 4 ->  5: disruption=0.20 (perfect=0.20); stddev/expected=0.0059
+# 5 ->  6: disruption=0.17 (perfect=0.17); stddev/expected=0.0030
+# 6 ->  7: disruption=0.14 (perfect=0.14); stddev/expected=0.0124
+# 7 ->  8: disruption=0.13 (perfect=0.12); stddev/expected=0.0072
+# 8 ->  9: disruption=0.11 (perfect=0.11); stddev/expected=0.0074
+# 9 -> 10: disruption=0.10 (perfect=0.10); stddev/expected=0.0161
+#10 -> 11: disruption=0.09 (perfect=0.09); stddev/expected=0.0055
+#11 -> 12: disruption=0.08 (perfect=0.08); stddev/expected=0.0092
+#12 -> 13: disruption=0.08 (perfect=0.08); stddev/expected=0.0134
+#13 -> 14: disruption=0.07 (perfect=0.07); stddev/expected=0.0124
+#14 -> 15: disruption=0.07 (perfect=0.07); stddev/expected=0.0156
+#15 -> 16: disruption=0.06 (perfect=0.06); stddev/expected=0.0182
+#16 -> 17: disruption=0.06 (perfect=0.06); stddev/expected=0.0150
+#17 -> 18: disruption=0.06 (perfect=0.06); stddev/expected=0.0109
+#18 -> 19: disruption=0.05 (perfect=0.05); stddev/expected=0.0162
+#19 -> 20: disruption=0.05 (perfect=0.05); stddev/expected=0.0149
+#20 -> 21: disruption=0.05 (perfect=0.05); stddev/expected=0.0148
+#21 -> 22: disruption=0.05 (perfect=0.05); stddev/expected=0.0230
+#22 -> 23: disruption=0.04 (perfect=0.04); stddev/expected=0.0208
+#23 -> 24: disruption=0.04 (perfect=0.04); stddev/expected=0.0210
+#24 -> 25: disruption=0.04 (perfect=0.04); stddev/expected=0.0228
+#25 -> 26: disruption=0.04 (perfect=0.04); stddev/expected=0.0155
+#26 -> 27: disruption=0.04 (perfect=0.04); stddev/expected=0.0208
+#27 -> 28: disruption=0.04 (perfect=0.04); stddev/expected=0.0218
+#28 -> 29: disruption=0.03 (perfect=0.03); stddev/expected=0.0193
+#29 -> 30: disruption=0.03 (perfect=0.03); stddev/expected=0.0169
+#30 -> 31: disruption=0.03 (perfect=0.03); stddev/expected=0.0163
+#31 -> 32: disruption=0.03 (perfect=0.03); stddev/expected=0.0192
+#32 -> 33: disruption=0.03 (perfect=0.03); stddev/expected=0.0212
+#33 -> 34: disruption=0.03 (perfect=0.03); stddev/expected=0.0240
+#34 -> 35: disruption=0.03 (perfect=0.03); stddev/expected=0.0227
+#35 -> 36: disruption=0.03 (perfect=0.03); stddev/expected=0.0230
+#36 -> 37: disruption=0.03 (perfect=0.03); stddev/expected=0.0183
+#37 -> 38: disruption=0.03 (perfect=0.03); stddev/expected=0.0227
+#38 -> 39: disruption=0.03 (perfect=0.03); stddev/expected=0.0255
+#39 -> 40: disruption=0.03 (perfect=0.03); stddev/expected=0.0247
+#40 -> 41: disruption=0.02 (perfect=0.02); stddev/expected=0.0228
+#41 -> 42: disruption=0.02 (perfect=0.02); stddev/expected=0.0247
+#42 -> 43: disruption=0.02 (perfect=0.02); stddev/expected=0.0265
+#43 -> 44: disruption=0.02 (perfect=0.02); stddev/expected=0.0250
+#44 -> 45: disruption=0.02 (perfect=0.02); stddev/expected=0.0258
+#45 -> 46: disruption=0.02 (perfect=0.02); stddev/expected=0.0196
+#46 -> 47: disruption=0.02 (perfect=0.02); stddev/expected=0.0235
+#47 -> 48: disruption=0.02 (perfect=0.02); stddev/expected=0.0314
+#48 -> 49: disruption=0.02 (perfect=0.02); stddev/expected=0.0293
+#49 -> 50: disruption=0.02 (perfect=0.02); stddev/expected=0.0241
+#50 -> 51: disruption=0.02 (perfect=0.02); stddev/expected=0.0291
+#51 -> 52: disruption=0.02 (perfect=0.02); stddev/expected=0.0304
+#52 -> 53: disruption=0.02 (perfect=0.02); stddev/expected=0.0307
+#53 -> 54: disruption=0.02 (perfect=0.02); stddev/expected=0.0250
+#54 -> 55: disruption=0.02 (perfect=0.02); stddev/expected=0.0290
+#55 -> 56: disruption=0.02 (perfect=0.02); stddev/expected=0.0284
+#56 -> 57: disruption=0.02 (perfect=0.02); stddev/expected=0.0272
+#57 -> 58: disruption=0.02 (perfect=0.02); stddev/expected=0.0272
+#58 -> 59: disruption=0.02 (perfect=0.02); stddev/expected=0.0304
+#59 -> 60: disruption=0.02 (perfect=0.02); stddev/expected=0.0345
+#60 -> 61: disruption=0.02 (perfect=0.02); stddev/expected=0.0251
+#61 -> 62: disruption=0.02 (perfect=0.02); stddev/expected=0.0249
+#62 -> 63: disruption=0.02 (perfect=0.02); stddev/expected=0.0285
+#63 -> 64: disruption=0.02 (perfect=0.02); stddev/expected=0.0285
+AT_CLEANUP
+
+AT_SETUP([iter_hash symmetric_l3 multipath link selection])
+AT_CHECK([[ovstest test-multipath 'symmetric_l3,50,iter_hash,1,0,NXM_NX_REG0[]']],
+  [0], [ignore])
+# 1 ->  2: disruption=0.50 (perfect=0.50); stddev/expected=0.0000
+# 2 ->  3: disruption=0.42 (perfect=0.33); stddev/expected=0.0034
+# 3 ->  4: disruption=0.25 (perfect=0.25); stddev/expected=0.0082
+# 4 ->  5: disruption=0.42 (perfect=0.20); stddev/expected=0.0073
+# 5 ->  6: disruption=0.17 (perfect=0.17); stddev/expected=0.0040
+# 6 ->  7: disruption=0.14 (perfect=0.14); stddev/expected=0.0069
+# 7 ->  8: disruption=0.13 (perfect=0.12); stddev/expected=0.0131
+# 8 ->  9: disruption=0.45 (perfect=0.11); stddev/expected=0.0093
+# 9 -> 10: disruption=0.10 (perfect=0.10); stddev/expected=0.0127
+#10 -> 11: disruption=0.09 (perfect=0.09); stddev/expected=0.0134
+#11 -> 12: disruption=0.08 (perfect=0.08); stddev/expected=0.0101
+#12 -> 13: disruption=0.08 (perfect=0.08); stddev/expected=0.0127
+#13 -> 14: disruption=0.07 (perfect=0.07); stddev/expected=0.0115
+#14 -> 15: disruption=0.07 (perfect=0.07); stddev/expected=0.0100
+#15 -> 16: disruption=0.06 (perfect=0.06); stddev/expected=0.0111
+#16 -> 17: disruption=0.47 (perfect=0.06); stddev/expected=0.0137
+#17 -> 18: disruption=0.05 (perfect=0.06); stddev/expected=0.0204
+#18 -> 19: disruption=0.05 (perfect=0.05); stddev/expected=0.0082
+#19 -> 20: disruption=0.05 (perfect=0.05); stddev/expected=0.0124
+#20 -> 21: disruption=0.05 (perfect=0.05); stddev/expected=0.0203
+#21 -> 22: disruption=0.05 (perfect=0.05); stddev/expected=0.0196
+#22 -> 23: disruption=0.04 (perfect=0.04); stddev/expected=0.0183
+#23 -> 24: disruption=0.04 (perfect=0.04); stddev/expected=0.0212
+#24 -> 25: disruption=0.04 (perfect=0.04); stddev/expected=0.0176
+#25 -> 26: disruption=0.04 (perfect=0.04); stddev/expected=0.0173
+#26 -> 27: disruption=0.04 (perfect=0.04); stddev/expected=0.0159
+#27 -> 28: disruption=0.03 (perfect=0.04); stddev/expected=0.0168
+#28 -> 29: disruption=0.03 (perfect=0.03); stddev/expected=0.0190
+#29 -> 30: disruption=0.03 (perfect=0.03); stddev/expected=0.0305
+#30 -> 31: disruption=0.03 (perfect=0.03); stddev/expected=0.0282
+#31 -> 32: disruption=0.03 (perfect=0.03); stddev/expected=0.0255
+#32 -> 33: disruption=0.49 (perfect=0.03); stddev/expected=0.0220
+#33 -> 34: disruption=0.03 (perfect=0.03); stddev/expected=0.0188
+#34 -> 35: disruption=0.03 (perfect=0.03); stddev/expected=0.0203
+#35 -> 36: disruption=0.03 (perfect=0.03); stddev/expected=0.0207
+#36 -> 37: disruption=0.03 (perfect=0.03); stddev/expected=0.0261
+#37 -> 38: disruption=0.03 (perfect=0.03); stddev/expected=0.0226
+#38 -> 39: disruption=0.03 (perfect=0.03); stddev/expected=0.0233
+#39 -> 40: disruption=0.03 (perfect=0.03); stddev/expected=0.0161
+#40 -> 41: disruption=0.03 (perfect=0.02); stddev/expected=0.0303
+#41 -> 42: disruption=0.02 (perfect=0.02); stddev/expected=0.0249
+#42 -> 43: disruption=0.02 (perfect=0.02); stddev/expected=0.0262
+#43 -> 44: disruption=0.02 (perfect=0.02); stddev/expected=0.0260
+#44 -> 45: disruption=0.02 (perfect=0.02); stddev/expected=0.0266
+#45 -> 46: disruption=0.02 (perfect=0.02); stddev/expected=0.0287
+#46 -> 47: disruption=0.02 (perfect=0.02); stddev/expected=0.0213
+#47 -> 48: disruption=0.02 (perfect=0.02); stddev/expected=0.0301
+#48 -> 49: disruption=0.02 (perfect=0.02); stddev/expected=0.0230
+#49 -> 50: disruption=0.02 (perfect=0.02); stddev/expected=0.0248
+#50 -> 51: disruption=0.02 (perfect=0.02); stddev/expected=0.0203
+#51 -> 52: disruption=0.02 (perfect=0.02); stddev/expected=0.0235
+#52 -> 53: disruption=0.02 (perfect=0.02); stddev/expected=0.0340
+#53 -> 54: disruption=0.02 (perfect=0.02); stddev/expected=0.0264
+#54 -> 55: disruption=0.02 (perfect=0.02); stddev/expected=0.0292
+#55 -> 56: disruption=0.02 (perfect=0.02); stddev/expected=0.0246
+#56 -> 57: disruption=0.02 (perfect=0.02); stddev/expected=0.0270
+#57 -> 58: disruption=0.02 (perfect=0.02); stddev/expected=0.0299
+#58 -> 59: disruption=0.02 (perfect=0.02); stddev/expected=0.0307
+#59 -> 60: disruption=0.02 (perfect=0.02); stddev/expected=0.0275
+#60 -> 61: disruption=0.02 (perfect=0.02); stddev/expected=0.0289
+#61 -> 62: disruption=0.02 (perfect=0.02); stddev/expected=0.0292
+#62 -> 63: disruption=0.02 (perfect=0.02); stddev/expected=0.0292
+#63 -> 64: disruption=0.02 (perfect=0.02); stddev/expected=0.0307
+AT_CLEANUP
diff --git a/tests/oss-fuzz/flow_extract_target.c b/tests/oss-fuzz/flow_extract_target.c
index 7cd3278..3ad1a96 100644
--- a/tests/oss-fuzz/flow_extract_target.c
+++ b/tests/oss-fuzz/flow_extract_target.c
@@ -382,6 +382,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
     hash = flow_hash_symmetric_l4(&flow, 0);
     hash = flow_hash_symmetric_l2(&flow, 0);
     hash = flow_hash_symmetric_l3l4(&flow, 0, NULL);
+    hash = flow_hash_symmetric_l3(&flow, 0);
     ignore(hash);
 
     /* Convert flow to match. */
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index fef6aa4..9df8aa3 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -409,17 +409,23 @@ actions=note:41.42.43,note:00.01.02.03.04.05.06.07,note
 tcp,tp_src=0x1230/0xfff0,tun_id=0x1234,cookie=0x5678,actions=flood
 actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel:0x123456789
 actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l4, 1024, iter_hash, 5000, 5050, reg0[0..12])
+actions=multipath(eth_src, 50, hrw, 12, 0, NXM_NX_REG0[0..3]),multipath(symmetric_l3, 1024, iter_hash, 5000, 5050, reg0[0..12])
 table=1,actions=drop
 tun_id=0x1234000056780000/0xffff0000ffff0000,actions=drop
 metadata=0x1234ffff5678ffff/0xffff0000ffff0000,actions=drop
 actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
+actions=bundle(symmetric_l3,60,hrw,ofport,slaves:2,3)
+actions=bundle(symmetric_l3,60,hrw,ofport,slaves:)
 actions=output:1,bundle(eth_src,0,hrw,ofport,slaves:1),output:2
 actions=bundle_load(eth_src,50,active_backup,ofport,reg0,slaves:1)
 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
 actions=bundle_load(symmetric_l4,60,hrw,ofport,reg0[0..15],slaves:[2,3])
 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
+actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
+actions=bundle_load(symmetric_l3,60,hrw,ofport,reg0[0..15],slaves:[2,3])
+actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
 actions=resubmit:1,resubmit(2),resubmit(,3),resubmit(2,3)
 send_flow_rem,actions=output:1,output:NXM_NX_REG0,output:2,output:reg1[16..31],output:3
@@ -459,17 +465,23 @@ NXT_FLOW_MOD: ADD table:255 actions=note:41.42.43.00.00.00,note:00.01.02.03.04.0
 NXT_FLOW_MOD: ADD table:255 tcp,tun_id=0x1234,tp_src=0x1230/0xfff0 cookie:0x5678 actions=FLOOD
 NXT_FLOW_MOD: ADD table:255 actions=set_tunnel:0x1234,set_tunnel64:0x9876,set_tunnel64:0x123456789
 NXT_FLOW_MOD: ADD table:255 actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l4,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
+NXT_FLOW_MOD: ADD table:255 actions=multipath(eth_src,50,hrw,12,0,NXM_NX_REG0[0..3]),multipath(symmetric_l3,1024,iter_hash,5000,5050,NXM_NX_REG0[0..12])
 NXT_FLOW_MOD: ADD table:1 actions=drop
 NXT_FLOW_MOD: ADD table:255 tun_id=0x1234000056780000/0xffff0000ffff0000 actions=drop
 NXT_FLOW_MOD: ADD table:255 metadata=0x1234000056780000/0xffff0000ffff0000 actions=drop
 NXT_FLOW_MOD: ADD table:255 actions=bundle(eth_src,50,active_backup,ofport,slaves:1)
 NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:2,3)
 NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l4,60,hrw,ofport,slaves:)
+NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l3,60,hrw,ofport,slaves:2,3)
+NXT_FLOW_MOD: ADD table:255 actions=bundle(symmetric_l3,60,hrw,ofport,slaves:)
 NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle(eth_src,0,hrw,ofport,slaves:1),output:2
 NXT_FLOW_MOD: ADD table:255 actions=bundle_load(eth_src,50,active_backup,ofport,NXM_NX_REG0[],slaves:1)
 NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
 NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
 NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l4,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
+NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
+NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..15],slaves:2,3)
+NXT_FLOW_MOD: ADD table:255 actions=bundle_load(symmetric_l3,60,hrw,ofport,NXM_NX_REG0[0..30],slaves:)
 NXT_FLOW_MOD: ADD table:255 actions=output:1,bundle_load(eth_src,0,hrw,ofport,NXM_NX_REG0[16..31],slaves:1),output:2
 NXT_FLOW_MOD: ADD table:255 actions=resubmit:1,resubmit:2,resubmit(,3),resubmit(2,3)
 NXT_FLOW_MOD: ADD table:255 send_flow_rem actions=output:1,output:NXM_NX_REG0[],output:2,output:NXM_NX_REG1[16..31],output:3
diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
index d6b6d28..aefea16 100644
--- a/utilities/ovs-ofctl.8.in
+++ b/utilities/ovs-ofctl.8.in
@@ -1421,6 +1421,8 @@ and IPv6, which hash to a constant zero.
 Like \fBsymmetric_l3l4+udp\fR, but UDP ports are included in the hash.
 This is a more effective hash when asymmetric UDP protocols such as
 VXLAN are not a consideration.
+.IP \fBsymmetric_l3\fR
+Hashes network source address and network destination address.
 .IP \fBnw_src\fR
 Hashes Network source address only.
 .IP \fBnw_dst\fR
-- 
1.8.3.1



More information about the dev mailing list