+flow_wildcards_combine(struct flow_wildcards *dst,
+ const struct flow_wildcards *src1,
+ const struct flow_wildcards *src2)
+{
+ int i;
+
+ dst->wildcards = src1->wildcards | src2->wildcards;
+ dst->tun_id_mask = src1->tun_id_mask & src2->tun_id_mask;
+ dst->nw_src_mask = src1->nw_src_mask & src2->nw_src_mask;
+ dst->nw_dst_mask = src1->nw_dst_mask & src2->nw_dst_mask;
+ dst->ipv6_src_mask = ipv6_addr_bitand(&src1->ipv6_src_mask,
+ &src2->ipv6_src_mask);
+ dst->ipv6_dst_mask = ipv6_addr_bitand(&src1->ipv6_dst_mask,
+ &src2->ipv6_dst_mask);
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ dst->reg_masks[i] = src1->reg_masks[i] & src2->reg_masks[i];
+ }
+ dst->vlan_tci_mask = src1->vlan_tci_mask & src2->vlan_tci_mask;
+}
+
+/* Returns a hash of the wildcards in 'wc'. */
+uint32_t
+flow_wildcards_hash(const struct flow_wildcards *wc)
+{
+ /* If you change struct flow_wildcards and thereby trigger this
+ * assertion, please check that the new struct flow_wildcards has no holes
+ * in it before you update the assertion. */
+ BUILD_ASSERT_DECL(sizeof *wc == 56 + FLOW_N_REGS * 4);
+ return hash_bytes(wc, sizeof *wc, 0);
+}
+
+/* Returns true if 'a' and 'b' represent the same wildcards, false if they are
+ * different. */
+bool
+flow_wildcards_equal(const struct flow_wildcards *a,
+ const struct flow_wildcards *b)
+{
+ int i;
+
+ if (a->wildcards != b->wildcards
+ || a->tun_id_mask != b->tun_id_mask
+ || a->nw_src_mask != b->nw_src_mask
+ || a->nw_dst_mask != b->nw_dst_mask
+ || a->vlan_tci_mask != b->vlan_tci_mask
+ || !ipv6_addr_equals(&a->ipv6_src_mask, &b->ipv6_src_mask)
+ || !ipv6_addr_equals(&a->ipv6_dst_mask, &b->ipv6_dst_mask)) {
+ return false;
+ }
+
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if (a->reg_masks[i] != b->reg_masks[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Returns true if at least one bit or field is wildcarded in 'a' but not in
+ * 'b', false otherwise. */
+bool
+flow_wildcards_has_extra(const struct flow_wildcards *a,
+ const struct flow_wildcards *b)
+{
+ int i;
+ struct in6_addr ipv6_masked;
+
+ for (i = 0; i < FLOW_N_REGS; i++) {
+ if ((a->reg_masks[i] & b->reg_masks[i]) != b->reg_masks[i]) {
+ return true;
+ }
+ }
+
+ ipv6_masked = ipv6_addr_bitand(&a->ipv6_src_mask, &b->ipv6_src_mask);
+ if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_src_mask)) {
+ return true;
+ }
+
+ ipv6_masked = ipv6_addr_bitand(&a->ipv6_dst_mask, &b->ipv6_dst_mask);
+ if (!ipv6_addr_equals(&ipv6_masked, &b->ipv6_dst_mask)) {
+ return true;
+ }
+
+ return (a->wildcards & ~b->wildcards
+ || (a->tun_id_mask & b->tun_id_mask) != b->tun_id_mask
+ || (a->nw_src_mask & b->nw_src_mask) != b->nw_src_mask
+ || (a->nw_dst_mask & b->nw_dst_mask) != b->nw_dst_mask
+ || (a->vlan_tci_mask & b->vlan_tci_mask) != b->vlan_tci_mask);
+}
+
+static bool
+set_nw_mask(ovs_be32 *maskp, ovs_be32 mask)
+{
+ if (ip_is_cidr(mask)) {
+ *maskp = mask;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/* Sets the IP (or ARP) source wildcard mask to CIDR 'mask' (consisting of N
+ * high-order 1-bit and 32-N low-order 0-bits). Returns true if successful,
+ * false if 'mask' is not a CIDR mask. */
+bool
+flow_wildcards_set_nw_src_mask(struct flow_wildcards *wc, ovs_be32 mask)
+{
+ return set_nw_mask(&wc->nw_src_mask, mask);
+}
+
+/* Sets the IP (or ARP) destination wildcard mask to CIDR 'mask' (consisting of
+ * N high-order 1-bit and 32-N low-order 0-bits). Returns true if successful,
+ * false if 'mask' is not a CIDR mask. */
+bool
+flow_wildcards_set_nw_dst_mask(struct flow_wildcards *wc, ovs_be32 mask)
+{
+ 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)