2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-28 21:07:47 +00:00
ovs/lib/classifier.c
Ben Pfaff d8ae4d6726 flow: Fully separate flow_wildcards from OpenFlow wildcard bits.
Originally, wildcards were just the OpenFlow OFPFW_* bits.  Then, when
OpenFlow added CIDR masks for IP addresses, struct flow_wildcards was born
with additional members for those masks, derived from the wildcard bits.
Then, when OVS added support for tunnels, we added another bit
NXFW_TUN_ID that coexisted with the OFPFW_*.  Later we added even more bits
that do not appear in the OpenFlow 1.0 match structure at all.  This had
become really confusing, and the difficulties were especially visible in
the long list of invariants in comments on struct flow_wildcards.

This commit cleanly separates the OpenFlow 1.0 wildcard bits from the
bits used inside Open vSwitch, by defining a new set of bits that are
used only internally to Open vSwitch and converting to and from those
wildcard bits at the point where data comes off or goes onto the wire.
It also moves those functions into ofp-util.[ch] since they are only for
dealing with OpenFlow wire protocol now.
2010-11-22 10:11:40 -08:00

853 lines
25 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2009, 2010 Nicira Networks.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "classifier.h"
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include "byte-order.h"
#include "dynamic-string.h"
#include "flow.h"
#include "hash.h"
#include "ofp-util.h"
#include "packets.h"
static struct cls_table *find_table(const struct classifier *,
const struct flow_wildcards *);
static struct cls_table *insert_table(struct classifier *,
const struct flow_wildcards *);
static struct cls_table *classifier_first_table(const struct classifier *);
static struct cls_table *classifier_next_table(const struct classifier *,
const struct cls_table *);
static void destroy_table(struct classifier *, struct cls_table *);
static struct cls_rule *find_match(const struct cls_table *,
const struct flow *);
static struct cls_rule *find_equal(struct cls_table *, const struct flow *,
uint32_t hash);
static struct cls_rule *insert_rule(struct cls_table *, struct cls_rule *);
static bool flow_equal_except(const struct flow *, const struct flow *,
const struct flow_wildcards *);
static void zero_wildcards(struct flow *, const struct flow_wildcards *);
/* Iterates RULE over HEAD and all of the cls_rules on HEAD->list. */
#define FOR_EACH_RULE_IN_LIST(RULE, HEAD) \
for ((RULE) = (HEAD); (RULE) != NULL; (RULE) = next_rule_in_list(RULE))
#define FOR_EACH_RULE_IN_LIST_SAFE(RULE, NEXT, HEAD) \
for ((RULE) = (HEAD); \
(RULE) != NULL && ((NEXT) = next_rule_in_list(RULE), true); \
(RULE) = (NEXT))
static struct cls_rule *next_rule_in_list__(struct cls_rule *);
static struct cls_rule *next_rule_in_list(struct cls_rule *);
static struct cls_table *
cls_table_from_hmap_node(const struct hmap_node *node)
{
return node ? CONTAINER_OF(node, struct cls_table, hmap_node) : NULL;
}
/* Converts the flow in 'flow' into a cls_rule in 'rule', with the given
* 'wildcards' and 'priority'. */
void
cls_rule_init(const struct flow *flow, const struct flow_wildcards *wildcards,
unsigned int priority, struct cls_rule *rule)
{
rule->flow = *flow;
rule->wc = *wildcards;
rule->priority = priority;
cls_rule_zero_wildcarded_fields(rule);
}
/* Converts the flow in 'flow' into an exact-match cls_rule in 'rule', with the
* given 'priority'. (For OpenFlow 1.0, exact-match rule are always highest
* priority, so 'priority' should be at least 65535.) */
void
cls_rule_init_exact(const struct flow *flow,
unsigned int priority, struct cls_rule *rule)
{
rule->flow = *flow;
flow_wildcards_init_exact(&rule->wc);
rule->priority = priority;
}
/* Initializes 'rule' as a "catch-all" rule that matches every packet, with
* priority 'priority'. */
void
cls_rule_init_catchall(struct cls_rule *rule, unsigned int priority)
{
memset(&rule->flow, 0, sizeof rule->flow);
flow_wildcards_init_catchall(&rule->wc);
rule->priority = priority;
}
/* For each bit or field wildcarded in 'rule', sets the corresponding bit or
* field in 'flow' to all-0-bits. It is important to maintain this invariant
* in a clr_rule that might be inserted into a classifier.
*
* It is never necessary to call this function directly for a cls_rule that is
* initialized or modified only by cls_rule_*() functions. It is useful to
* restore the invariant in a cls_rule whose 'wc' member is modified by hand.
*/
void
cls_rule_zero_wildcarded_fields(struct cls_rule *rule)
{
zero_wildcards(&rule->flow, &rule->wc);
}
void
cls_rule_set_in_port(struct cls_rule *rule, uint16_t odp_port)
{
rule->wc.wildcards &= ~FWW_IN_PORT;
rule->flow.in_port = odp_port;
}
void
cls_rule_set_dl_type(struct cls_rule *rule, ovs_be16 dl_type)
{
rule->wc.wildcards &= ~FWW_DL_TYPE;
rule->flow.dl_type = dl_type;
}
void
cls_rule_set_dl_src(struct cls_rule *rule, const uint8_t dl_src[ETH_ADDR_LEN])
{
rule->wc.wildcards &= ~FWW_DL_SRC;
memcpy(rule->flow.dl_src, dl_src, ETH_ADDR_LEN);
}
void
cls_rule_set_dl_dst(struct cls_rule *rule, const uint8_t dl_dst[ETH_ADDR_LEN])
{
rule->wc.wildcards &= ~(FWW_DL_DST | FWW_ETH_MCAST);
memcpy(rule->flow.dl_dst, dl_dst, ETH_ADDR_LEN);
}
bool
cls_rule_set_dl_tci(struct cls_rule *rule, ovs_be16 tci)
{
return cls_rule_set_dl_tci_masked(rule, tci, htons(0xffff));
}
bool
cls_rule_set_dl_tci_masked(struct cls_rule *rule, ovs_be16 tci, ovs_be16 mask)
{
switch (ntohs(mask)) {
case 0xffff:
if (tci == htons(0)) {
/* Match only packets that have no 802.1Q header. */
rule->wc.wildcards &= ~(FWW_DL_VLAN | FWW_DL_VLAN_PCP);
rule->flow.dl_vlan = htons(OFP_VLAN_NONE);
rule->flow.dl_vlan_pcp = 0;
return true;
} else if (tci & htons(VLAN_CFI)) {
/* Match only packets that have a specific 802.1Q VID and PCP. */
rule->wc.wildcards &= ~(FWW_DL_VLAN | FWW_DL_VLAN_PCP);
rule->flow.dl_vlan = htons(vlan_tci_to_vid(tci));
rule->flow.dl_vlan_pcp = vlan_tci_to_pcp(tci);
return true;
} else {
/* Impossible. */
return false;
}
case 0x1fff:
if (!(tci & htons(VLAN_CFI))) {
return false;
} else {
/* Match only packets that have a specific 802.1Q VID. */
cls_rule_set_dl_vlan(rule, tci & htons(VLAN_VID_MASK));
rule->wc.wildcards |= FWW_DL_VLAN_PCP;
rule->flow.dl_vlan_pcp = 0;
return true;
}
case 0xf000:
if (!(tci & htons(VLAN_CFI))) {
return false;
} else {
/* Match only packets that have a specific 802.1Q PCP. */
cls_rule_set_dl_vlan_pcp(rule, vlan_tci_to_pcp(tci));
rule->wc.wildcards |= FWW_DL_VLAN;
rule->flow.dl_vlan = 0;
return true;
}
case 0x0000:
/* Match anything. */
rule->wc.wildcards |= FWW_DL_VLAN | FWW_DL_VLAN_PCP;
rule->flow.dl_vlan = htons(0);
rule->flow.dl_vlan_pcp = 0;
return true;
default:
return false;
}
}
void
cls_rule_set_dl_vlan(struct cls_rule *rule, ovs_be16 dl_vlan)
{
if (dl_vlan != htons(OFP_VLAN_NONE)) {
dl_vlan &= htons(VLAN_VID_MASK);
}
rule->wc.wildcards &= ~FWW_DL_VLAN;
rule->flow.dl_vlan = dl_vlan;
}
void
cls_rule_set_dl_vlan_pcp(struct cls_rule *rule, uint8_t dl_vlan_pcp)
{
rule->wc.wildcards &= ~FWW_DL_VLAN_PCP;
rule->flow.dl_vlan_pcp = dl_vlan_pcp & 0x07;
}
void
cls_rule_set_tp_src(struct cls_rule *rule, ovs_be16 tp_src)
{
rule->wc.wildcards &= ~FWW_TP_SRC;
rule->flow.tp_src = tp_src;
}
void
cls_rule_set_tp_dst(struct cls_rule *rule, ovs_be16 tp_dst)
{
rule->wc.wildcards &= ~FWW_TP_DST;
rule->flow.tp_dst = tp_dst;
}
void
cls_rule_set_nw_proto(struct cls_rule *rule, uint8_t nw_proto)
{
rule->wc.wildcards &= ~FWW_NW_PROTO;
rule->flow.nw_proto = nw_proto;
}
void
cls_rule_set_nw_src(struct cls_rule *rule, ovs_be32 nw_src)
{
cls_rule_set_nw_src_masked(rule, nw_src, htonl(UINT32_MAX));
}
bool
cls_rule_set_nw_src_masked(struct cls_rule *rule, ovs_be32 ip, ovs_be32 mask)
{
if (flow_wildcards_set_nw_src_mask(&rule->wc, mask)) {
rule->flow.nw_src = ip & mask;
return true;
} else {
return false;
}
}
void
cls_rule_set_nw_dst(struct cls_rule *rule, ovs_be32 nw_dst)
{
cls_rule_set_nw_dst_masked(rule, nw_dst, htonl(UINT32_MAX));
}
bool
cls_rule_set_nw_dst_masked(struct cls_rule *rule, ovs_be32 ip, ovs_be32 mask)
{
if (flow_wildcards_set_nw_dst_mask(&rule->wc, mask)) {
rule->flow.nw_dst = ip & mask;
return true;
} else {
return false;
}
}
void
cls_rule_set_nw_tos(struct cls_rule *rule, uint8_t nw_tos)
{
rule->wc.wildcards &= ~FWW_NW_TOS;
rule->flow.nw_tos = nw_tos & IP_DSCP_MASK;
}
void
cls_rule_set_icmp_type(struct cls_rule *rule, uint8_t icmp_type)
{
rule->wc.wildcards &= ~FWW_TP_SRC;
rule->flow.icmp_type = htons(icmp_type);
}
void
cls_rule_set_icmp_code(struct cls_rule *rule, uint8_t icmp_code)
{
rule->wc.wildcards &= ~FWW_TP_DST;
rule->flow.icmp_code = htons(icmp_code);
}
/* Returns true if 'a' and 'b' have the same priority, wildcard the same
* fields, and have the same values for fixed fields, otherwise false. */
bool
cls_rule_equal(const struct cls_rule *a, const struct cls_rule *b)
{
return (a->priority == b->priority
&& flow_wildcards_equal(&a->wc, &b->wc)
&& flow_equal(&a->flow, &b->flow));
}
/* Converts 'rule' to a string and returns the string. The caller must free
* the string (with free()). */
char *
cls_rule_to_string(const struct cls_rule *rule)
{
struct ds s = DS_EMPTY_INITIALIZER;
ds_put_format(&s, "wildcards=%x priority=%u ",
rule->wc.wildcards, rule->priority);
flow_format(&s, &rule->flow);
return ds_cstr(&s);
}
/* Prints cls_rule 'rule', for debugging.
*
* (The output could be improved and expanded, but this was good enough to
* debug the classifier.) */
void
cls_rule_print(const struct cls_rule *rule)
{
printf("wildcards=%x priority=%u ", rule->wc.wildcards, rule->priority);
flow_print(stdout, &rule->flow);
putc('\n', stdout);
}
/* Initializes 'cls' as a classifier that initially contains no classification
* rules. */
void
classifier_init(struct classifier *cls)
{
cls->n_rules = 0;
hmap_init(&cls->tables);
}
/* Destroys 'cls'. Rules within 'cls', if any, are not freed; this is the
* caller's responsibility. */
void
classifier_destroy(struct classifier *cls)
{
if (cls) {
struct cls_table *table, *next_table;
HMAP_FOR_EACH_SAFE (table, next_table, hmap_node, &cls->tables) {
hmap_destroy(&table->rules);
hmap_remove(&cls->tables, &table->hmap_node);
free(table);
}
hmap_destroy(&cls->tables);
}
}
/* Returns true if 'cls' contains no classification rules, false otherwise. */
bool
classifier_is_empty(const struct classifier *cls)
{
return cls->n_rules == 0;
}
/* Returns the number of rules in 'classifier'. */
int
classifier_count(const struct classifier *cls)
{
return cls->n_rules;
}
/* Inserts 'rule' into 'cls'. Until 'rule' is removed from 'cls', the caller
* must not modify or free it.
*
* If 'cls' already contains an identical rule (including wildcards, values of
* fixed fields, and priority), replaces the old rule by 'rule' and returns the
* rule that was replaced. The caller takes ownership of the returned rule and
* is thus responsible for freeing it, etc., as necessary.
*
* Returns NULL if 'cls' does not contain a rule with an identical key, after
* inserting the new rule. In this case, no rules are displaced by the new
* rule, even rules that cannot have any effect because the new rule matches a
* superset of their flows and has higher priority. */
struct cls_rule *
classifier_insert(struct classifier *cls, struct cls_rule *rule)
{
struct cls_rule *old_rule;
struct cls_table *table;
table = find_table(cls, &rule->wc);
if (!table) {
table = insert_table(cls, &rule->wc);
}
old_rule = insert_rule(table, rule);
if (!old_rule) {
table->n_table_rules++;
cls->n_rules++;
}
return old_rule;
}
/* Removes 'rule' from 'cls'. It is the caller's responsibility to free
* 'rule', if this is desirable. */
void
classifier_remove(struct classifier *cls, struct cls_rule *rule)
{
struct cls_rule *head;
struct cls_table *table;
table = find_table(cls, &rule->wc);
head = find_equal(table, &rule->flow, rule->hmap_node.hash);
if (head != rule) {
list_remove(&rule->list);
} else if (list_is_empty(&rule->list)) {
hmap_remove(&table->rules, &rule->hmap_node);
} else {
struct cls_rule *next = CONTAINER_OF(rule->list.next,
struct cls_rule, list);
list_remove(&rule->list);
hmap_replace(&table->rules, &rule->hmap_node, &next->hmap_node);
}
if (--table->n_table_rules == 0) {
destroy_table(cls, table);
}
cls->n_rules--;
}
/* Finds and returns the highest-priority rule in 'cls' that matches 'flow'.
* Returns a null pointer if no rules in 'cls' match 'flow'. If multiple rules
* of equal priority match 'flow', returns one arbitrarily. */
struct cls_rule *
classifier_lookup(const struct classifier *cls, const struct flow *flow)
{
struct cls_table *table;
struct cls_rule *best;
best = NULL;
HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
struct cls_rule *rule = find_match(table, flow);
if (rule && (!best || rule->priority > best->priority)) {
best = rule;
}
}
return best;
}
/* Finds and returns a rule in 'cls' with exactly the same priority and
* matching criteria as 'target'. Returns a null pointer if 'cls' doesn't
* contain an exact match.
*
* Priority is ignored for exact-match rules (because OpenFlow 1.0 always
* treats exact-match rules as highest priority). */
struct cls_rule *
classifier_find_rule_exactly(const struct classifier *cls,
const struct cls_rule *target)
{
struct cls_rule *head, *rule;
struct cls_table *table;
table = find_table(cls, &target->wc);
if (!table) {
return NULL;
}
head = find_equal(table, &target->flow, flow_hash(&target->flow, 0));
if (flow_wildcards_is_exact(&target->wc)) {
return head;
}
FOR_EACH_RULE_IN_LIST (rule, head) {
if (target->priority >= rule->priority) {
return target->priority == rule->priority ? rule : NULL;
}
}
return NULL;
}
/* Checks if 'target' would overlap any other rule in 'cls'. Two rules are
* considered to overlap if both rules have the same priority and a packet
* could match both. */
bool
classifier_rule_overlaps(const struct classifier *cls,
const struct cls_rule *target)
{
struct cls_table *table;
HMAP_FOR_EACH (table, hmap_node, &cls->tables) {
struct flow_wildcards wc;
struct cls_rule *head;
flow_wildcards_combine(&wc, &target->wc, &table->wc);
HMAP_FOR_EACH (head, hmap_node, &table->rules) {
struct cls_rule *rule;
FOR_EACH_RULE_IN_LIST (rule, head) {
if (rule->priority == target->priority
&& flow_equal_except(&target->flow, &rule->flow, &wc)) {
return true;
}
}
}
}
return false;
}
/* Iteration. */
static bool
rule_matches(const struct cls_rule *rule, const struct cls_rule *target)
{
return (!target
|| flow_equal_except(&rule->flow, &target->flow, &target->wc));
}
static struct cls_rule *
search_table(const struct cls_table *table, const struct cls_rule *target)
{
if (!target || !flow_wildcards_has_extra(&table->wc, &target->wc)) {
struct cls_rule *rule;
HMAP_FOR_EACH (rule, hmap_node, &table->rules) {
if (rule_matches(rule, target)) {
return rule;
}
}
}
return NULL;
}
/* Initializes 'cursor' for iterating through 'cls' rules that exactly match
* 'target' or are more specific than 'target'. That is, a given 'rule'
* matches 'target' if, for every field:
*
* - 'target' and 'rule' specify the same (non-wildcarded) value for the
* field, or
*
* - 'target' wildcards the field,
*
* but not if:
*
* - 'target' and 'rule' specify different values for the field, or
*
* - 'target' specifies a value for the field but 'rule' wildcards it.
*
* Equivalently, the truth table for whether a field matches is:
*
* rule
*
* wildcard exact
* +---------+---------+
* t wild | yes | yes |
* a card | | |
* r +---------+---------+
* g exact | no |if values|
* e | |are equal|
* t +---------+---------+
*
* This is the matching rule used by OpenFlow 1.0 non-strict OFPT_FLOW_MOD
* commands and by OpenFlow 1.0 aggregate and flow stats.
*
* Ignores target->priority.
*
* 'target' may be NULL to iterate over every rule in 'cls'. */
void
cls_cursor_init(struct cls_cursor *cursor, const struct classifier *cls,
const struct cls_rule *target)
{
cursor->cls = cls;
cursor->target = target;
}
/* Returns the first matching cls_rule in 'cursor''s iteration, or a null
* pointer if there are no matches. */
struct cls_rule *
cls_cursor_first(struct cls_cursor *cursor)
{
struct cls_table *table;
for (table = classifier_first_table(cursor->cls); table;
table = classifier_next_table(cursor->cls, table)) {
struct cls_rule *rule = search_table(table, cursor->target);
if (rule) {
cursor->table = table;
return rule;
}
}
return NULL;
}
/* Returns the next matching cls_rule in 'cursor''s iteration, or a null
* pointer if there are no more matches. */
struct cls_rule *
cls_cursor_next(struct cls_cursor *cursor, struct cls_rule *rule)
{
const struct cls_table *table;
struct cls_rule *next;
next = next_rule_in_list__(rule);
if (next->priority < rule->priority) {
return next;
}
/* 'next' is the head of the list, that is, the rule that is included in
* the table's hmap. (This is important when the classifier contains rules
* that differ only in priority.) */
rule = next;
HMAP_FOR_EACH_CONTINUE (rule, hmap_node, &cursor->table->rules) {
if (rule_matches(rule, cursor->target)) {
return rule;
}
}
for (table = classifier_next_table(cursor->cls, cursor->table); table;
table = classifier_next_table(cursor->cls, table)) {
rule = search_table(table, cursor->target);
if (rule) {
cursor->table = table;
return rule;
}
}
return NULL;
}
static struct cls_table *
find_table(const struct classifier *cls, const struct flow_wildcards *wc)
{
struct cls_table *table;
HMAP_FOR_EACH_IN_BUCKET (table, hmap_node, flow_wildcards_hash(wc),
&cls->tables) {
if (flow_wildcards_equal(wc, &table->wc)) {
return table;
}
}
return NULL;
}
static struct cls_table *
insert_table(struct classifier *cls, const struct flow_wildcards *wc)
{
struct cls_table *table;
table = xzalloc(sizeof *table);
hmap_init(&table->rules);
table->wc = *wc;
hmap_insert(&cls->tables, &table->hmap_node, flow_wildcards_hash(wc));
return table;
}
static struct cls_table *
classifier_first_table(const struct classifier *cls)
{
return cls_table_from_hmap_node(hmap_first(&cls->tables));
}
static struct cls_table *
classifier_next_table(const struct classifier *cls,
const struct cls_table *table)
{
return cls_table_from_hmap_node(hmap_next(&cls->tables,
&table->hmap_node));
}
static void
destroy_table(struct classifier *cls, struct cls_table *table)
{
hmap_remove(&cls->tables, &table->hmap_node);
hmap_destroy(&table->rules);
free(table);
}
static struct cls_rule *
find_match(const struct cls_table *table, const struct flow *flow)
{
struct cls_rule *rule;
struct flow f;
f = *flow;
zero_wildcards(&f, &table->wc);
HMAP_FOR_EACH_WITH_HASH (rule, hmap_node, flow_hash(&f, 0),
&table->rules) {
if (flow_equal(&f, &rule->flow)) {
return rule;
}
}
return NULL;
}
static struct cls_rule *
find_equal(struct cls_table *table, const struct flow *flow, uint32_t hash)
{
struct cls_rule *head;
HMAP_FOR_EACH_WITH_HASH (head, hmap_node, hash, &table->rules) {
if (flow_equal(&head->flow, flow)) {
return head;
}
}
return NULL;
}
static struct cls_rule *
insert_rule(struct cls_table *table, struct cls_rule *new)
{
struct cls_rule *head;
new->hmap_node.hash = flow_hash(&new->flow, 0);
head = find_equal(table, &new->flow, new->hmap_node.hash);
if (!head) {
hmap_insert(&table->rules, &new->hmap_node, new->hmap_node.hash);
list_init(&new->list);
return NULL;
} else {
/* Scan the list for the insertion point that will keep the list in
* order of decreasing priority. */
struct cls_rule *rule;
FOR_EACH_RULE_IN_LIST (rule, head) {
if (new->priority >= rule->priority) {
if (rule == head) {
/* 'new' is the new highest-priority flow in the list. */
hmap_replace(&table->rules,
&rule->hmap_node, &new->hmap_node);
}
if (new->priority == rule->priority) {
list_replace(&new->list, &rule->list);
return rule;
} else {
list_insert(&rule->list, &new->list);
return NULL;
}
}
}
/* Insert 'new' at the end of the list. */
list_push_back(&head->list, &new->list);
return NULL;
}
}
static struct cls_rule *
next_rule_in_list__(struct cls_rule *rule)
{
struct cls_rule *next = OBJECT_CONTAINING(rule->list.next, next, list);
return next;
}
static struct cls_rule *
next_rule_in_list(struct cls_rule *rule)
{
struct cls_rule *next = next_rule_in_list__(rule);
return next->priority < rule->priority ? next : NULL;
}
static bool
flow_equal_except(const struct flow *a, const struct flow *b,
const struct flow_wildcards *wildcards)
{
const flow_wildcards_t wc = wildcards->wildcards;
int i;
BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + FLOW_N_REGS * 4);
for (i = 0; i < FLOW_N_REGS; i++) {
if ((a->regs[i] ^ b->regs[i]) & wildcards->reg_masks[i]) {
return false;
}
}
return ((wc & FWW_TUN_ID || a->tun_id == b->tun_id)
&& !((a->nw_src ^ b->nw_src) & wildcards->nw_src_mask)
&& !((a->nw_dst ^ b->nw_dst) & wildcards->nw_dst_mask)
&& (wc & FWW_IN_PORT || a->in_port == b->in_port)
&& (wc & FWW_DL_VLAN || a->dl_vlan == b->dl_vlan)
&& (wc & FWW_DL_TYPE || a->dl_type == b->dl_type)
&& (wc & FWW_TP_SRC || a->tp_src == b->tp_src)
&& (wc & FWW_TP_DST || a->tp_dst == b->tp_dst)
&& (wc & FWW_DL_SRC || eth_addr_equals(a->dl_src, b->dl_src))
&& (wc & FWW_DL_DST
|| (!((a->dl_dst[0] ^ b->dl_dst[0]) & 0xfe)
&& a->dl_dst[1] == b->dl_dst[1]
&& a->dl_dst[2] == b->dl_dst[2]
&& a->dl_dst[3] == b->dl_dst[3]
&& a->dl_dst[4] == b->dl_dst[4]
&& a->dl_dst[5] == b->dl_dst[5]))
&& (wc & FWW_ETH_MCAST
|| !((a->dl_dst[0] ^ b->dl_dst[0]) & 0x01))
&& (wc & FWW_NW_PROTO || a->nw_proto == b->nw_proto)
&& (wc & FWW_DL_VLAN_PCP || a->dl_vlan_pcp == b->dl_vlan_pcp)
&& (wc & FWW_NW_TOS || a->nw_tos == b->nw_tos));
}
static void
zero_wildcards(struct flow *flow, const struct flow_wildcards *wildcards)
{
const flow_wildcards_t wc = wildcards->wildcards;
int i;
BUILD_ASSERT_DECL(FLOW_SIG_SIZE == 37 + 4 * FLOW_N_REGS);
for (i = 0; i < FLOW_N_REGS; i++) {
flow->regs[i] &= wildcards->reg_masks[i];
}
if (wc & FWW_TUN_ID) {
flow->tun_id = 0;
}
flow->nw_src &= wildcards->nw_src_mask;
flow->nw_dst &= wildcards->nw_dst_mask;
if (wc & FWW_IN_PORT) {
flow->in_port = 0;
}
if (wc & FWW_DL_VLAN) {
flow->dl_vlan = 0;
}
if (wc & FWW_DL_TYPE) {
flow->dl_type = 0;
}
if (wc & FWW_TP_SRC) {
flow->tp_src = 0;
}
if (wc & FWW_TP_DST) {
flow->tp_dst = 0;
}
if (wc & FWW_DL_SRC) {
memset(flow->dl_src, 0, sizeof flow->dl_src);
}
if (wc & FWW_DL_DST) {
flow->dl_dst[0] &= 0x01;
memset(&flow->dl_dst[1], 0, 5);
}
if (wc & FWW_ETH_MCAST) {
flow->dl_dst[0] &= 0xfe;
}
if (wc & FWW_NW_PROTO) {
flow->nw_proto = 0;
}
if (wc & FWW_DL_VLAN_PCP) {
flow->dl_vlan_pcp = 0;
}
if (wc & FWW_NW_TOS) {
flow->nw_tos = 0;
}
}