Jasper Capel jasper@capel.tv
Jean Tourrilhes jt@hpl.hp.com
Jeremy Stribling strib@nicira.com
+Jeroen van Bemmel jvb127@gmail.com
Jesse Gross jesse@nicira.com
Jing Ai jinga@google.com
Joe Perches joe@perches.com
* OpenFlow 1.4+ OFPMP_TABLE_DESC is now implemented.
- Support for matching and generating options with Geneve tunnels.
- Support Multicast Listener Discovery (MLDv1 and MLDv2).
+ - Add 'symmetric_l3l4' and 'symmetric_l3l4+udp' hash functions.
v2.4.0 - xx xxx xxxx
* - NXM_OF_IP_SRC / NXM_OF_IP_DST
* - NXM_OF_TCP_SRC / NXM_OF_TCP_DST
*/
- NX_HASH_FIELDS_SYMMETRIC_L4
+ NX_HASH_FIELDS_SYMMETRIC_L4,
+
+ /* L3+L4 only, including the following fields:
+ *
+ * - NXM_OF_IP_PROTO
+ * - NXM_OF_IP_SRC / NXM_OF_IP_DST
+ * - NXM_OF_SCTP_SRC / NXM_OF_SCTP_DST
+ * - NXM_OF_TCP_SRC / NXM_OF_TCP_DST
+ */
+ NX_HASH_FIELDS_SYMMETRIC_L3L4,
+
+ /* L3+L4 only with UDP ports, including the following fields:
+ *
+ * - NXM_OF_IP_PROTO
+ * - NXM_OF_IP_SRC / NXM_OF_IP_DST
+ * - NXM_OF_SCTP_SRC / NXM_OF_SCTP_DST
+ * - NXM_OF_TCP_SRC / NXM_OF_TCP_DST
+ * - NXM_OF_UDP_SRC / NXM_OF_UDP_DST
+ */
+ NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP
+
+
};
/* This command enables or disables an Open vSwitch extension that allows a
bundle->fields = NX_HASH_FIELDS_ETH_SRC;
} else if (!strcasecmp(fields, "symmetric_l4")) {
bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
+ } else if (!strcasecmp(fields, "symmetric_l3l4")) {
+ bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4;
+ } else if (!strcasecmp(fields, "symmetric_l3l4+udp")) {
+ bundle->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP;
} else {
return xasprintf("%s: unknown fields `%s'", s, fields);
}
return jhash_bytes(&fields, sizeof fields, basis);
}
+/* Hashes 'flow' based on its L3 through L4 protocol information */
+uint32_t
+flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis,
+ bool inc_udp_ports)
+{
+ uint32_t hash = basis;
+
+ /* UDP source and destination port are also taken into account. */
+ if (flow->dl_type == htons(ETH_TYPE_IP)) {
+ hash = hash_add(hash,
+ (OVS_FORCE uint32_t) (flow->nw_src ^ flow->nw_dst));
+ } else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
+ /* IPv6 addresses are 64-bit aligned inside struct flow. */
+ const uint64_t *a = ALIGNED_CAST(uint64_t *, flow->ipv6_src.s6_addr);
+ const uint64_t *b = ALIGNED_CAST(uint64_t *, flow->ipv6_dst.s6_addr);
+
+ for (int i = 0; i < 4; i++) {
+ hash = hash_add64(hash, a[i] ^ b[i]);
+ }
+ } else {
+ /* Cannot hash non-IP flows */
+ return 0;
+ }
+
+ hash = hash_add(hash, flow->nw_proto);
+ if (flow->nw_proto == IPPROTO_TCP || flow->nw_proto == IPPROTO_SCTP ||
+ (inc_udp_ports && flow->nw_proto == IPPROTO_UDP)) {
+ hash = hash_add(hash,
+ (OVS_FORCE uint16_t) (flow->tp_src ^ flow->tp_dst));
+ }
+
+ return hash_finish(hash, basis);
+}
+
/* Initialize a flow with random fields that matter for nx_hash_fields. */
void
flow_random_hash_fields(struct flow *flow)
wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
break;
+ case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP:
+ if (is_ip_any(flow) && flow->nw_proto == IPPROTO_UDP) {
+ memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+ memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+ }
+ /* no break */
+ case NX_HASH_FIELDS_SYMMETRIC_L3L4:
+ 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);
+ } else {
+ break; /* non-IP flow */
+ }
+
+ memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
+ if (flow->nw_proto == IPPROTO_TCP || flow->nw_proto == IPPROTO_SCTP) {
+ memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
+ memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
+ }
+ break;
+
default:
OVS_NOT_REACHED();
}
case NX_HASH_FIELDS_SYMMETRIC_L4:
return flow_hash_symmetric_l4(flow, basis);
+
+ case NX_HASH_FIELDS_SYMMETRIC_L3L4:
+ return flow_hash_symmetric_l3l4(flow, basis, false);
+
+ case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP:
+ return flow_hash_symmetric_l3l4(flow, basis, true);
+
}
OVS_NOT_REACHED();
switch (fields) {
case NX_HASH_FIELDS_ETH_SRC: return "eth_src";
case NX_HASH_FIELDS_SYMMETRIC_L4: return "symmetric_l4";
+ case NX_HASH_FIELDS_SYMMETRIC_L3L4: return "symmetric_l3l4";
+ case NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP: return "symmetric_l3l4+udp";
default: return "<unknown>";
}
}
flow_hash_fields_valid(enum nx_hash_fields fields)
{
return fields == NX_HASH_FIELDS_ETH_SRC
- || fields == NX_HASH_FIELDS_SYMMETRIC_L4;
+ || fields == NX_HASH_FIELDS_SYMMETRIC_L4
+ || fields == NX_HASH_FIELDS_SYMMETRIC_L3L4
+ || fields == NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP;
}
/* Returns a hash value for the bits of 'flow' that are active based on
const struct flow_wildcards *);
uint32_t flow_hash_5tuple(const struct flow *flow, uint32_t basis);
uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis);
+uint32_t flow_hash_symmetric_l3l4(const struct flow *flow, uint32_t basis,
+ bool inc_udp_ports );
/* Initialize a flow with random fields that matter for nx_hash_fields. */
void flow_random_hash_fields(struct flow *);
mp->fields = NX_HASH_FIELDS_ETH_SRC;
} else if (!strcasecmp(fields, "symmetric_l4")) {
mp->fields = NX_HASH_FIELDS_SYMMETRIC_L4;
+ } else if (!strcasecmp(fields, "symmetric_l3l4")) {
+ mp->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4;
+ } else if (!strcasecmp(fields, "symmetric_l3l4+udp")) {
+ mp->fields = NX_HASH_FIELDS_SYMMETRIC_L3L4_UDP;
} else {
return xasprintf("%s: unknown fields `%s'", s_, fields);
}
\fIdst\fB[\fIstart\fB..\fIend\fB]\fR, which must be an NXM field as
described above.
.IP
-Currently, \fIfields\fR must be either \fBeth_src\fR or
-\fBsymmetric_l4\fR and \fIalgorithm\fR must be one of \fBmodulo_n\fR,
+\fIfields\fR must be one of the following:
+.RS
+.IP \fBeth_src\fR
+Hashes Ethernet source address only.
+.IP \fBsymmetric_l4\fR
+Hashes Ethernet source, destination, and type, VLAN ID, IPv4/IPv6
+source, destination, and protocol, and TCP or SCTP (but not UDP)
+ports. The hash is computed so that pairs of corresponding flows in
+each direction hash to the same value, in environments where L2 paths
+are the same in each direction. UDP ports are not included in the
+hash to support protocols such as VXLAN that use asymmetric ports in
+each direction.
+.IP \fBsymmetric_l3l4\fR
+Hashes IPv4/IPv6 source, destination, and protocol, and TCP or SCTP
+(but not UDP) ports. Like \fBsymmetric_l4\fR, this is a symmetric
+hash, but by excluding L2 headers it is more effective in environments
+with asymmetric L2 paths (e.g. paths involving VRRP IP addresses on a
+router). Not an effective hash function for protocols other than IPv4
+and IPv6, which hash to a constant zero.
+.IP \fBsymmetric_l3l4+udp\fR
+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.
+.RE
+.IP
+\fIalgorithm\fR must be one of \fBmodulo_n\fR,
\fBhash_threshold\fR, \fBhrw\fR, and \fBiter_hash\fR. Only
the \fBiter_hash\fR algorithm uses \fIarg\fR.
.IP
\fIslave_type\fR is \fBofport\fR. Thus, each \fIs1\fR through \fIsN\fR should
be an OpenFlow port number. Outputs to the selected slave.
.IP
-Currently, \fIfields\fR must be either \fBeth_src\fR or \fBsymmetric_l4\fR and
-\fIalgorithm\fR must be one of \fBhrw\fR and \fBactive_backup\fR.
+Currently, \fIfields\fR must be either \fBeth_src\fR, \fBsymmetric_l4\fR, \fBsymmetric_l3l4\fR, or \fBsymmetric_l3l4+udp\fR,
+and \fIalgorithm\fR must be one of \fBhrw\fR and \fBactive_backup\fR.
.IP
Example: \fBbundle(eth_src,0,hrw,ofport,slaves:4,8)\fR uses an Ethernet source
hash with basis 0, to select between OpenFlow ports 4 and 8 using the Highest