+ return set_nw_mask(&wc->nw_dst_mask, mask);
+}
+
+static bool
+set_ipv6_mask(struct in6_addr *maskp, const struct in6_addr *mask)
+{
+ if (ipv6_is_cidr(mask)) {
+ *maskp = *mask;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* Sets the IPv6 source wildcard mask to CIDR 'mask' (consisting of N
+ * high-order 1-bit and 128-N low-order 0-bits). Returns true if successful,
+ * false if 'mask' is not a CIDR mask. */
+bool
+flow_wildcards_set_ipv6_src_mask(struct flow_wildcards *wc,
+ const struct in6_addr *mask)
+{
+ return set_ipv6_mask(&wc->ipv6_src_mask, mask);
+}
+
+/* Sets the IPv6 destination wildcard mask to CIDR 'mask' (consisting of
+ * N high-order 1-bit and 128-N low-order 0-bits). Returns true if
+ * successful, false if 'mask' is not a CIDR mask. */
+bool
+flow_wildcards_set_ipv6_dst_mask(struct flow_wildcards *wc,
+ const struct in6_addr *mask)
+{
+ return set_ipv6_mask(&wc->ipv6_dst_mask, mask);
+}
+
+/* Sets the wildcard mask for register 'idx' in 'wc' to 'mask'.
+ * (A 0-bit indicates a wildcard bit.) */
+void
+flow_wildcards_set_reg_mask(struct flow_wildcards *wc, int idx, uint32_t mask)
+{
+ wc->reg_masks[idx] = mask;
+}
+
+/* Returns the wildcard bitmask for the Ethernet destination address
+ * that 'wc' specifies. The bitmask has a 0 in each bit that is wildcarded
+ * and a 1 in each bit that must match. */
+const uint8_t *
+flow_wildcards_to_dl_dst_mask(flow_wildcards_t wc)
+{
+ static const uint8_t no_wild[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const uint8_t addr_wild[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
+ static const uint8_t mcast_wild[] = {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const uint8_t all_wild[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+ switch (wc & (FWW_DL_DST | FWW_ETH_MCAST)) {
+ case 0: return no_wild;
+ case FWW_DL_DST: return addr_wild;
+ case FWW_ETH_MCAST: return mcast_wild;
+ case FWW_DL_DST | FWW_ETH_MCAST: return all_wild;
+ }
+ NOT_REACHED();
+}
+
+/* Returns true if 'mask' is a valid wildcard bitmask for the Ethernet
+ * destination address. Valid bitmasks are either all-bits-0 or all-bits-1,
+ * except that the multicast bit may differ from the rest of the bits. So,
+ * there are four possible valid bitmasks:
+ *
+ * - 00:00:00:00:00:00
+ * - 01:00:00:00:00:00
+ * - fe:ff:ff:ff:ff:ff
+ * - ff:ff:ff:ff:ff:ff
+ *
+ * All other bitmasks are invalid. */
+bool
+flow_wildcards_is_dl_dst_mask_valid(const uint8_t mask[ETH_ADDR_LEN])
+{
+ switch (mask[0]) {
+ case 0x00:
+ case 0x01:
+ return (mask[1] | mask[2] | mask[3] | mask[4] | mask[5]) == 0x00;
+
+ case 0xfe:
+ case 0xff:
+ return (mask[1] & mask[2] & mask[3] & mask[4] & mask[5]) == 0xff;
+
+ default:
+ return false;
+ }
+}
+
+/* Returns 'wc' with the FWW_DL_DST and FWW_ETH_MCAST bits modified
+ * appropriately to match 'mask'.
+ *
+ * This function will assert-fail if 'mask' is invalid. Only 'mask' values
+ * accepted by flow_wildcards_is_dl_dst_mask_valid() are allowed. */
+flow_wildcards_t
+flow_wildcards_set_dl_dst_mask(flow_wildcards_t wc,
+ const uint8_t mask[ETH_ADDR_LEN])
+{
+ assert(flow_wildcards_is_dl_dst_mask_valid(mask));
+
+ switch (mask[0]) {
+ case 0x00:
+ return wc | FWW_DL_DST | FWW_ETH_MCAST;
+
+ case 0x01:
+ return (wc | FWW_DL_DST) & ~FWW_ETH_MCAST;
+
+ case 0xfe:
+ return (wc & ~FWW_DL_DST) | FWW_ETH_MCAST;
+
+ case 0xff:
+ return wc & ~(FWW_DL_DST | FWW_ETH_MCAST);
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+/* Hashes 'flow' based on its L2 through L4 protocol information. */
+uint32_t
+flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis)
+{
+ struct {
+ union {
+ ovs_be32 ipv4_addr;
+ struct in6_addr ipv6_addr;
+ };
+ ovs_be16 eth_type;
+ ovs_be16 vlan_tci;
+ ovs_be16 tp_addr;
+ uint8_t eth_addr[ETH_ADDR_LEN];
+ uint8_t ip_proto;
+ } fields;
+
+ int i;
+
+ memset(&fields, 0, sizeof fields);
+ for (i = 0; i < ETH_ADDR_LEN; i++) {
+ fields.eth_addr[i] = flow->dl_src[i] ^ flow->dl_dst[i];
+ }
+ fields.vlan_tci = flow->vlan_tci & htons(VLAN_VID_MASK);
+ fields.eth_type = flow->dl_type;
+
+ /* UDP source and destination port are not taken into account because they
+ * will not necessarily be symmetric in a bidirectional flow. */
+ if (fields.eth_type == htons(ETH_TYPE_IP)) {
+ fields.ipv4_addr = flow->nw_src ^ flow->nw_dst;
+ fields.ip_proto = flow->nw_proto;
+ if (fields.ip_proto == IPPROTO_TCP) {
+ fields.tp_addr = flow->tp_src ^ flow->tp_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];
+ }
+ fields.ip_proto = flow->nw_proto;
+ if (fields.ip_proto == IPPROTO_TCP) {
+ fields.tp_addr = flow->tp_src ^ flow->tp_dst;
+ }
+ }
+ return hash_bytes(&fields, sizeof fields, basis);
+}
+
+/* Hashes the portions of 'flow' designated by 'fields'. */
+uint32_t
+flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields,
+ uint16_t basis)
+{
+ switch (fields) {
+
+ case NX_HASH_FIELDS_ETH_SRC:
+ return hash_bytes(flow->dl_src, sizeof flow->dl_src, basis);
+
+ case NX_HASH_FIELDS_SYMMETRIC_L4:
+ return flow_hash_symmetric_l4(flow, basis);
+ }
+
+ NOT_REACHED();
+}
+
+/* Returns a string representation of 'fields'. */
+const char *
+flow_hash_fields_to_str(enum nx_hash_fields fields)
+{
+ switch (fields) {
+ case NX_HASH_FIELDS_ETH_SRC: return "eth_src";
+ case NX_HASH_FIELDS_SYMMETRIC_L4: return "symmetric_l4";
+ default: return "<unknown>";
+ }
+}
+
+/* Returns true if the value of 'fields' is supported. Otherwise false. */
+bool
+flow_hash_fields_valid(enum nx_hash_fields fields)
+{
+ return fields == NX_HASH_FIELDS_ETH_SRC
+ || fields == NX_HASH_FIELDS_SYMMETRIC_L4;