2009-07-08 13:19:16 -07:00
|
|
|
|
/*
|
2014-04-01 00:47:01 -07:00
|
|
|
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* 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:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* 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.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* "White box" tests for classifier.
|
|
|
|
|
*
|
|
|
|
|
* With very few exceptions, these tests obtain complete coverage of every
|
|
|
|
|
* basic block and every branch in the classifier implementation, e.g. a clean
|
|
|
|
|
* report from "gcov -b". (Covering the exceptions would require finding
|
|
|
|
|
* collisions in the hash function used for flow data, etc.)
|
|
|
|
|
*
|
|
|
|
|
* This test should receive a clean report from "valgrind --leak-check=full":
|
|
|
|
|
* it frees every heap block that it allocates.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <limits.h>
|
2010-10-28 17:13:18 -07:00
|
|
|
|
#include "byte-order.h"
|
2010-05-26 12:48:32 -07:00
|
|
|
|
#include "command-line.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "flow.h"
|
2010-11-22 10:09:18 -08:00
|
|
|
|
#include "ofp-util.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "packets.h"
|
2013-06-25 09:22:11 -07:00
|
|
|
|
#include "random.h"
|
2010-10-28 13:20:23 -07:00
|
|
|
|
#include "unaligned.h"
|
2014-04-01 00:47:01 -07:00
|
|
|
|
#include "ovstest.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#undef NDEBUG
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
2014-04-29 15:50:38 -07:00
|
|
|
|
/* We need access to classifier internal definitions to be able to fully
|
|
|
|
|
* test them. The alternative would be to expose them all in the classifier
|
|
|
|
|
* API. */
|
|
|
|
|
#include "classifier.c"
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
/* Fields in a rule. */
|
2010-11-10 14:39:54 -08:00
|
|
|
|
#define CLS_FIELDS \
|
2012-09-13 20:11:08 -07:00
|
|
|
|
/* struct flow all-caps */ \
|
|
|
|
|
/* member name name */ \
|
|
|
|
|
/* ----------- -------- */ \
|
|
|
|
|
CLS_FIELD(tunnel.tun_id, TUN_ID) \
|
|
|
|
|
CLS_FIELD(metadata, METADATA) \
|
|
|
|
|
CLS_FIELD(nw_src, NW_SRC) \
|
|
|
|
|
CLS_FIELD(nw_dst, NW_DST) \
|
|
|
|
|
CLS_FIELD(in_port, IN_PORT) \
|
|
|
|
|
CLS_FIELD(vlan_tci, VLAN_TCI) \
|
|
|
|
|
CLS_FIELD(dl_type, DL_TYPE) \
|
|
|
|
|
CLS_FIELD(tp_src, TP_SRC) \
|
|
|
|
|
CLS_FIELD(tp_dst, TP_DST) \
|
|
|
|
|
CLS_FIELD(dl_src, DL_SRC) \
|
|
|
|
|
CLS_FIELD(dl_dst, DL_DST) \
|
|
|
|
|
CLS_FIELD(nw_proto, NW_PROTO) \
|
|
|
|
|
CLS_FIELD(nw_tos, NW_DSCP)
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
|
|
|
|
/* Field indexes.
|
|
|
|
|
*
|
|
|
|
|
* (These are also indexed into struct classifier's 'tables' array.) */
|
|
|
|
|
enum {
|
2012-08-03 13:27:15 -07:00
|
|
|
|
#define CLS_FIELD(MEMBER, NAME) CLS_F_IDX_##NAME,
|
2010-11-03 11:00:58 -07:00
|
|
|
|
CLS_FIELDS
|
|
|
|
|
#undef CLS_FIELD
|
|
|
|
|
CLS_N_FIELDS
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Field information. */
|
|
|
|
|
struct cls_field {
|
|
|
|
|
int ofs; /* Offset in struct flow. */
|
|
|
|
|
int len; /* Length in bytes. */
|
|
|
|
|
const char *name; /* Name (for debugging). */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct cls_field cls_fields[CLS_N_FIELDS] = {
|
2012-08-03 13:27:15 -07:00
|
|
|
|
#define CLS_FIELD(MEMBER, NAME) \
|
2010-11-03 11:00:58 -07:00
|
|
|
|
{ offsetof(struct flow, MEMBER), \
|
|
|
|
|
sizeof ((struct flow *)0)->MEMBER, \
|
|
|
|
|
#NAME },
|
|
|
|
|
CLS_FIELDS
|
|
|
|
|
#undef CLS_FIELD
|
|
|
|
|
};
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct test_rule {
|
|
|
|
|
int aux; /* Auxiliary data. */
|
|
|
|
|
struct cls_rule cls_rule; /* Classifier rule data. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct test_rule *
|
|
|
|
|
test_rule_from_cls_rule(const struct cls_rule *rule)
|
|
|
|
|
{
|
|
|
|
|
return rule ? CONTAINER_OF(rule, struct test_rule, cls_rule) : NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-21 14:59:35 -08:00
|
|
|
|
static void
|
|
|
|
|
test_rule_destroy(struct test_rule *rule)
|
|
|
|
|
{
|
|
|
|
|
if (rule) {
|
|
|
|
|
cls_rule_destroy(&rule->cls_rule);
|
|
|
|
|
free(rule);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-20 11:29:43 -07:00
|
|
|
|
static struct test_rule *make_rule(int wc_fields, unsigned int priority,
|
|
|
|
|
int value_pat);
|
|
|
|
|
static void free_rule(struct test_rule *);
|
|
|
|
|
static struct test_rule *clone_rule(const struct test_rule *);
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
/* Trivial (linear) classifier. */
|
|
|
|
|
struct tcls {
|
|
|
|
|
size_t n_rules;
|
|
|
|
|
size_t allocated_rules;
|
|
|
|
|
struct test_rule **rules;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tcls_init(struct tcls *tcls)
|
|
|
|
|
{
|
|
|
|
|
tcls->n_rules = 0;
|
|
|
|
|
tcls->allocated_rules = 0;
|
|
|
|
|
tcls->rules = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tcls_destroy(struct tcls *tcls)
|
|
|
|
|
{
|
|
|
|
|
if (tcls) {
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tcls->n_rules; i++) {
|
2012-12-21 14:59:35 -08:00
|
|
|
|
test_rule_destroy(tcls->rules[i]);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
free(tcls->rules);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
tcls_is_empty(const struct tcls *tcls)
|
|
|
|
|
{
|
|
|
|
|
return tcls->n_rules == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct test_rule *
|
|
|
|
|
tcls_insert(struct tcls *tcls, const struct test_rule *rule)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tcls->n_rules; i++) {
|
|
|
|
|
const struct cls_rule *pos = &tcls->rules[i]->cls_rule;
|
2010-11-08 16:35:34 -08:00
|
|
|
|
if (cls_rule_equal(pos, &rule->cls_rule)) {
|
|
|
|
|
/* Exact match. */
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(tcls->rules[i]);
|
|
|
|
|
tcls->rules[i] = clone_rule(rule);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return tcls->rules[i];
|
2010-03-19 13:54:36 -04:00
|
|
|
|
} else if (pos->priority < rule->cls_rule.priority) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tcls->n_rules >= tcls->allocated_rules) {
|
|
|
|
|
tcls->rules = x2nrealloc(tcls->rules, &tcls->allocated_rules,
|
|
|
|
|
sizeof *tcls->rules);
|
|
|
|
|
}
|
|
|
|
|
if (i != tcls->n_rules) {
|
|
|
|
|
memmove(&tcls->rules[i + 1], &tcls->rules[i],
|
|
|
|
|
sizeof *tcls->rules * (tcls->n_rules - i));
|
|
|
|
|
}
|
2012-08-20 11:29:43 -07:00
|
|
|
|
tcls->rules[i] = clone_rule(rule);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
tcls->n_rules++;
|
|
|
|
|
return tcls->rules[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tcls_remove(struct tcls *cls, const struct test_rule *rule)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cls->n_rules; i++) {
|
|
|
|
|
struct test_rule *pos = cls->rules[i];
|
|
|
|
|
if (pos == rule) {
|
2012-12-21 14:59:35 -08:00
|
|
|
|
test_rule_destroy(pos);
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
memmove(&cls->rules[i], &cls->rules[i + 1],
|
|
|
|
|
sizeof *cls->rules * (cls->n_rules - i - 1));
|
2012-12-21 14:59:35 -08:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
cls->n_rules--;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
2012-09-04 12:43:53 -07:00
|
|
|
|
match(const struct cls_rule *wild_, const struct flow *fixed)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2012-09-04 12:43:53 -07:00
|
|
|
|
struct match wild;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int f_idx;
|
|
|
|
|
|
2012-09-04 12:43:53 -07:00
|
|
|
|
minimatch_expand(&wild_->match, &wild);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
for (f_idx = 0; f_idx < CLS_N_FIELDS; f_idx++) {
|
2010-11-10 14:39:54 -08:00
|
|
|
|
bool eq;
|
|
|
|
|
|
2012-08-03 13:27:15 -07:00
|
|
|
|
if (f_idx == CLS_F_IDX_NW_SRC) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->nw_src ^ wild.flow.nw_src)
|
|
|
|
|
& wild.wc.masks.nw_src);
|
2010-11-10 14:39:54 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_NW_DST) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->nw_dst ^ wild.flow.nw_dst)
|
|
|
|
|
& wild.wc.masks.nw_dst);
|
2012-01-27 17:16:05 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_TP_SRC) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->tp_src ^ wild.flow.tp_src)
|
|
|
|
|
& wild.wc.masks.tp_src);
|
2012-01-27 17:16:05 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_TP_DST) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->tp_dst ^ wild.flow.tp_dst)
|
|
|
|
|
& wild.wc.masks.tp_dst);
|
2012-05-29 00:38:21 +12:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_DL_SRC) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = eth_addr_equal_except(fixed->dl_src, wild.flow.dl_src,
|
|
|
|
|
wild.wc.masks.dl_src);
|
2012-05-29 00:38:21 +12:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_DL_DST) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = eth_addr_equal_except(fixed->dl_dst, wild.flow.dl_dst,
|
|
|
|
|
wild.wc.masks.dl_dst);
|
2010-11-23 10:06:28 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_VLAN_TCI) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->vlan_tci ^ wild.flow.vlan_tci)
|
|
|
|
|
& wild.wc.masks.vlan_tci);
|
2011-01-20 15:29:00 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_TUN_ID) {
|
2012-09-13 20:11:08 -07:00
|
|
|
|
eq = !((fixed->tunnel.tun_id ^ wild.flow.tunnel.tun_id)
|
|
|
|
|
& wild.wc.masks.tunnel.tun_id);
|
2012-07-12 00:01:11 +12:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_METADATA) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->metadata ^ wild.flow.metadata)
|
|
|
|
|
& wild.wc.masks.metadata);
|
2011-11-09 23:39:16 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_NW_DSCP) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->nw_tos ^ wild.flow.nw_tos) &
|
|
|
|
|
(wild.wc.masks.nw_tos & IP_DSCP_MASK));
|
2012-06-18 14:12:52 -07:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_NW_PROTO) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->nw_proto ^ wild.flow.nw_proto)
|
|
|
|
|
& wild.wc.masks.nw_proto);
|
2012-06-18 13:33:13 -07:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_DL_TYPE) {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
eq = !((fixed->dl_type ^ wild.flow.dl_type)
|
|
|
|
|
& wild.wc.masks.dl_type);
|
2012-08-03 13:27:15 -07:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_IN_PORT) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
eq = !((fixed->in_port.ofp_port
|
|
|
|
|
^ wild.flow.in_port.ofp_port)
|
|
|
|
|
& wild.wc.masks.in_port.ofp_port);
|
2010-11-10 14:39:54 -08:00
|
|
|
|
} else {
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 14:39:54 -08:00
|
|
|
|
if (!eq) {
|
|
|
|
|
return false;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct cls_rule *
|
2010-10-28 13:26:31 -07:00
|
|
|
|
tcls_lookup(const struct tcls *cls, const struct flow *flow)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cls->n_rules; i++) {
|
|
|
|
|
struct test_rule *pos = cls->rules[i];
|
2010-10-28 13:26:31 -07:00
|
|
|
|
if (match(&pos->cls_rule, flow)) {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return &pos->cls_rule;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-10-28 13:26:31 -07:00
|
|
|
|
tcls_delete_matches(struct tcls *cls, const struct cls_rule *target)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cls->n_rules; ) {
|
|
|
|
|
struct test_rule *pos = cls->rules[i];
|
2012-09-04 12:43:53 -07:00
|
|
|
|
if (!minimask_has_extra(&pos->cls_rule.match.mask,
|
|
|
|
|
&target->match.mask)) {
|
|
|
|
|
struct flow flow;
|
|
|
|
|
|
|
|
|
|
miniflow_expand(&pos->cls_rule.match.flow, &flow);
|
|
|
|
|
if (match(target, &flow)) {
|
|
|
|
|
tcls_remove(cls, pos);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2012-09-04 12:43:53 -07:00
|
|
|
|
i++;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-28 13:26:31 -07:00
|
|
|
|
static ovs_be32 nw_src_values[] = { CONSTANT_HTONL(0xc0a80001),
|
2010-05-11 16:44:03 -07:00
|
|
|
|
CONSTANT_HTONL(0xc0a04455) };
|
2010-10-28 13:26:31 -07:00
|
|
|
|
static ovs_be32 nw_dst_values[] = { CONSTANT_HTONL(0xc0a80002),
|
2010-05-11 16:44:03 -07:00
|
|
|
|
CONSTANT_HTONL(0xc0a04455) };
|
2010-12-10 10:42:42 -08:00
|
|
|
|
static ovs_be64 tun_id_values[] = {
|
|
|
|
|
0,
|
|
|
|
|
CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
|
2012-07-12 00:01:11 +12:00
|
|
|
|
static ovs_be64 metadata_values[] = {
|
|
|
|
|
0,
|
|
|
|
|
CONSTANT_HTONLL(UINT64_C(0xfedcba9876543210)) };
|
2013-06-19 16:58:44 -07:00
|
|
|
|
static ofp_port_t in_port_values[] = { OFP_PORT_C(1), OFPP_LOCAL };
|
2010-11-23 10:06:28 -08:00
|
|
|
|
static ovs_be16 vlan_tci_values[] = { CONSTANT_HTONS(101), CONSTANT_HTONS(0) };
|
2010-10-28 13:26:31 -07:00
|
|
|
|
static ovs_be16 dl_type_values[]
|
2010-05-11 16:44:03 -07:00
|
|
|
|
= { CONSTANT_HTONS(ETH_TYPE_IP), CONSTANT_HTONS(ETH_TYPE_ARP) };
|
2010-10-28 13:26:31 -07:00
|
|
|
|
static ovs_be16 tp_src_values[] = { CONSTANT_HTONS(49362),
|
2010-05-11 16:44:03 -07:00
|
|
|
|
CONSTANT_HTONS(80) };
|
2010-10-28 13:26:31 -07:00
|
|
|
|
static ovs_be16 tp_dst_values[] = { CONSTANT_HTONS(6667), CONSTANT_HTONS(22) };
|
2014-10-22 14:58:43 +08:00
|
|
|
|
static uint8_t dl_src_values[][ETH_ADDR_LEN] = {
|
|
|
|
|
{ 0x00, 0x02, 0xe3, 0x0f, 0x80, 0xa4 },
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{ 0x5e, 0x33, 0x7f, 0x5f, 0x1e, 0x99 } };
|
2014-10-22 14:58:43 +08:00
|
|
|
|
static uint8_t dl_dst_values[][ETH_ADDR_LEN] = {
|
|
|
|
|
{ 0x4a, 0x27, 0x71, 0xae, 0x64, 0xc1 },
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } };
|
2011-02-02 11:33:20 -08:00
|
|
|
|
static uint8_t nw_proto_values[] = { IPPROTO_TCP, IPPROTO_ICMP };
|
2011-11-09 23:39:16 -08:00
|
|
|
|
static uint8_t nw_dscp_values[] = { 48, 0 };
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
static void *values[CLS_N_FIELDS][2];
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
init_values(void)
|
|
|
|
|
{
|
2010-04-12 11:49:16 -04:00
|
|
|
|
values[CLS_F_IDX_TUN_ID][0] = &tun_id_values[0];
|
|
|
|
|
values[CLS_F_IDX_TUN_ID][1] = &tun_id_values[1];
|
|
|
|
|
|
2012-07-12 00:01:11 +12:00
|
|
|
|
values[CLS_F_IDX_METADATA][0] = &metadata_values[0];
|
|
|
|
|
values[CLS_F_IDX_METADATA][1] = &metadata_values[1];
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
values[CLS_F_IDX_IN_PORT][0] = &in_port_values[0];
|
|
|
|
|
values[CLS_F_IDX_IN_PORT][1] = &in_port_values[1];
|
|
|
|
|
|
2010-11-23 10:06:28 -08:00
|
|
|
|
values[CLS_F_IDX_VLAN_TCI][0] = &vlan_tci_values[0];
|
|
|
|
|
values[CLS_F_IDX_VLAN_TCI][1] = &vlan_tci_values[1];
|
2009-11-11 14:59:49 -08:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
values[CLS_F_IDX_DL_SRC][0] = dl_src_values[0];
|
|
|
|
|
values[CLS_F_IDX_DL_SRC][1] = dl_src_values[1];
|
|
|
|
|
|
|
|
|
|
values[CLS_F_IDX_DL_DST][0] = dl_dst_values[0];
|
|
|
|
|
values[CLS_F_IDX_DL_DST][1] = dl_dst_values[1];
|
|
|
|
|
|
|
|
|
|
values[CLS_F_IDX_DL_TYPE][0] = &dl_type_values[0];
|
|
|
|
|
values[CLS_F_IDX_DL_TYPE][1] = &dl_type_values[1];
|
|
|
|
|
|
|
|
|
|
values[CLS_F_IDX_NW_SRC][0] = &nw_src_values[0];
|
|
|
|
|
values[CLS_F_IDX_NW_SRC][1] = &nw_src_values[1];
|
|
|
|
|
|
|
|
|
|
values[CLS_F_IDX_NW_DST][0] = &nw_dst_values[0];
|
|
|
|
|
values[CLS_F_IDX_NW_DST][1] = &nw_dst_values[1];
|
|
|
|
|
|
|
|
|
|
values[CLS_F_IDX_NW_PROTO][0] = &nw_proto_values[0];
|
|
|
|
|
values[CLS_F_IDX_NW_PROTO][1] = &nw_proto_values[1];
|
|
|
|
|
|
2011-11-09 23:39:16 -08:00
|
|
|
|
values[CLS_F_IDX_NW_DSCP][0] = &nw_dscp_values[0];
|
|
|
|
|
values[CLS_F_IDX_NW_DSCP][1] = &nw_dscp_values[1];
|
2010-01-21 17:34:05 -08:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
values[CLS_F_IDX_TP_SRC][0] = &tp_src_values[0];
|
|
|
|
|
values[CLS_F_IDX_TP_SRC][1] = &tp_src_values[1];
|
|
|
|
|
|
|
|
|
|
values[CLS_F_IDX_TP_DST][0] = &tp_dst_values[0];
|
|
|
|
|
values[CLS_F_IDX_TP_DST][1] = &tp_dst_values[1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define N_NW_SRC_VALUES ARRAY_SIZE(nw_src_values)
|
|
|
|
|
#define N_NW_DST_VALUES ARRAY_SIZE(nw_dst_values)
|
2010-04-12 11:49:16 -04:00
|
|
|
|
#define N_TUN_ID_VALUES ARRAY_SIZE(tun_id_values)
|
2012-07-12 00:01:11 +12:00
|
|
|
|
#define N_METADATA_VALUES ARRAY_SIZE(metadata_values)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#define N_IN_PORT_VALUES ARRAY_SIZE(in_port_values)
|
2010-11-23 10:06:28 -08:00
|
|
|
|
#define N_VLAN_TCI_VALUES ARRAY_SIZE(vlan_tci_values)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#define N_DL_TYPE_VALUES ARRAY_SIZE(dl_type_values)
|
|
|
|
|
#define N_TP_SRC_VALUES ARRAY_SIZE(tp_src_values)
|
|
|
|
|
#define N_TP_DST_VALUES ARRAY_SIZE(tp_dst_values)
|
|
|
|
|
#define N_DL_SRC_VALUES ARRAY_SIZE(dl_src_values)
|
|
|
|
|
#define N_DL_DST_VALUES ARRAY_SIZE(dl_dst_values)
|
|
|
|
|
#define N_NW_PROTO_VALUES ARRAY_SIZE(nw_proto_values)
|
2011-11-09 23:39:16 -08:00
|
|
|
|
#define N_NW_DSCP_VALUES ARRAY_SIZE(nw_dscp_values)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
#define N_FLOW_VALUES (N_NW_SRC_VALUES * \
|
|
|
|
|
N_NW_DST_VALUES * \
|
2010-04-12 11:49:16 -04:00
|
|
|
|
N_TUN_ID_VALUES * \
|
2009-07-08 13:19:16 -07:00
|
|
|
|
N_IN_PORT_VALUES * \
|
2010-11-23 10:06:28 -08:00
|
|
|
|
N_VLAN_TCI_VALUES * \
|
2009-07-08 13:19:16 -07:00
|
|
|
|
N_DL_TYPE_VALUES * \
|
|
|
|
|
N_TP_SRC_VALUES * \
|
|
|
|
|
N_TP_DST_VALUES * \
|
|
|
|
|
N_DL_SRC_VALUES * \
|
|
|
|
|
N_DL_DST_VALUES * \
|
2010-01-21 17:34:05 -08:00
|
|
|
|
N_NW_PROTO_VALUES * \
|
2011-11-09 23:39:16 -08:00
|
|
|
|
N_NW_DSCP_VALUES)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
|
get_value(unsigned int *x, unsigned n_values)
|
|
|
|
|
{
|
|
|
|
|
unsigned int rem = *x % n_values;
|
|
|
|
|
*x /= n_values;
|
|
|
|
|
return rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
compare_classifiers(struct classifier *cls, struct tcls *tcls)
|
|
|
|
|
{
|
2010-05-26 15:24:13 -07:00
|
|
|
|
static const int confidence = 500;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
assert(classifier_count(cls) == tcls->n_rules);
|
2010-05-26 15:24:13 -07:00
|
|
|
|
for (i = 0; i < confidence; i++) {
|
2013-11-19 17:31:29 -08:00
|
|
|
|
struct cls_rule *cr0, *cr1, *cr2;
|
2010-09-03 11:30:02 -07:00
|
|
|
|
struct flow flow;
|
2013-11-19 17:31:29 -08:00
|
|
|
|
struct flow_wildcards wc;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
unsigned int x;
|
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
flow_wildcards_init_catchall(&wc);
|
2013-06-25 09:22:11 -07:00
|
|
|
|
x = random_range(N_FLOW_VALUES);
|
2012-06-18 15:09:49 -07:00
|
|
|
|
memset(&flow, 0, sizeof flow);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
flow.nw_src = nw_src_values[get_value(&x, N_NW_SRC_VALUES)];
|
|
|
|
|
flow.nw_dst = nw_dst_values[get_value(&x, N_NW_DST_VALUES)];
|
2012-09-13 20:11:08 -07:00
|
|
|
|
flow.tunnel.tun_id = tun_id_values[get_value(&x, N_TUN_ID_VALUES)];
|
2012-07-12 00:01:11 +12:00
|
|
|
|
flow.metadata = metadata_values[get_value(&x, N_METADATA_VALUES)];
|
2013-06-19 16:58:44 -07:00
|
|
|
|
flow.in_port.ofp_port = in_port_values[get_value(&x,
|
|
|
|
|
N_IN_PORT_VALUES)];
|
2010-11-23 10:06:28 -08:00
|
|
|
|
flow.vlan_tci = vlan_tci_values[get_value(&x, N_VLAN_TCI_VALUES)];
|
2009-07-08 13:19:16 -07:00
|
|
|
|
flow.dl_type = dl_type_values[get_value(&x, N_DL_TYPE_VALUES)];
|
|
|
|
|
flow.tp_src = tp_src_values[get_value(&x, N_TP_SRC_VALUES)];
|
|
|
|
|
flow.tp_dst = tp_dst_values[get_value(&x, N_TP_DST_VALUES)];
|
|
|
|
|
memcpy(flow.dl_src, dl_src_values[get_value(&x, N_DL_SRC_VALUES)],
|
|
|
|
|
ETH_ADDR_LEN);
|
|
|
|
|
memcpy(flow.dl_dst, dl_dst_values[get_value(&x, N_DL_DST_VALUES)],
|
|
|
|
|
ETH_ADDR_LEN);
|
|
|
|
|
flow.nw_proto = nw_proto_values[get_value(&x, N_NW_PROTO_VALUES)];
|
2011-11-09 23:39:16 -08:00
|
|
|
|
flow.nw_tos = nw_dscp_values[get_value(&x, N_NW_DSCP_VALUES)];
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2014-06-23 10:13:41 -07:00
|
|
|
|
/* This assertion is here to suppress a GCC 4.9 array-bounds warning */
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_assert(cls->n_tries <= CLS_MAX_TRIES);
|
2014-06-23 10:13:41 -07:00
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
cr0 = classifier_lookup(cls, &flow, &wc);
|
2010-10-28 13:26:31 -07:00
|
|
|
|
cr1 = tcls_lookup(tcls, &flow);
|
|
|
|
|
assert((cr0 == NULL) == (cr1 == NULL));
|
|
|
|
|
if (cr0 != NULL) {
|
|
|
|
|
const struct test_rule *tr0 = test_rule_from_cls_rule(cr0);
|
|
|
|
|
const struct test_rule *tr1 = test_rule_from_cls_rule(cr1);
|
|
|
|
|
|
2010-11-08 16:35:34 -08:00
|
|
|
|
assert(cls_rule_equal(cr0, cr1));
|
2010-10-28 13:26:31 -07:00
|
|
|
|
assert(tr0->aux == tr1->aux);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2013-11-19 17:31:29 -08:00
|
|
|
|
cr2 = classifier_lookup(cls, &flow, NULL);
|
|
|
|
|
assert(cr2 == cr0);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
destroy_classifier(struct classifier *cls)
|
|
|
|
|
{
|
2014-07-21 21:00:04 -07:00
|
|
|
|
struct test_rule *rule;
|
2010-10-28 16:18:20 -07:00
|
|
|
|
|
2014-07-21 21:00:04 -07:00
|
|
|
|
CLS_FOR_EACH_SAFE (rule, cls_rule, cls) {
|
2010-10-28 16:18:20 -07:00
|
|
|
|
classifier_remove(cls, &rule->cls_rule);
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(rule);
|
2010-10-28 16:18:20 -07:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
classifier_destroy(cls);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-26 07:41:25 -07:00
|
|
|
|
static void
|
2014-07-18 02:24:26 -07:00
|
|
|
|
pvector_verify(const struct pvector *pvec)
|
2014-06-26 07:41:25 -07:00
|
|
|
|
{
|
|
|
|
|
void *ptr OVS_UNUSED;
|
|
|
|
|
unsigned int priority, prev_priority = UINT_MAX;
|
|
|
|
|
|
|
|
|
|
PVECTOR_FOR_EACH (ptr, pvec) {
|
|
|
|
|
priority = cursor__.vector[cursor__.entry_idx].priority;
|
|
|
|
|
if (priority > prev_priority) {
|
|
|
|
|
VLOG_ABORT("Priority vector is out of order (%u > %u)",
|
|
|
|
|
priority, prev_priority);
|
|
|
|
|
}
|
|
|
|
|
prev_priority = priority;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-11 02:29:08 -07:00
|
|
|
|
static unsigned int
|
|
|
|
|
trie_verify(const rcu_trie_ptr *trie, unsigned int ofs, unsigned int n_bits)
|
|
|
|
|
{
|
|
|
|
|
const struct trie_node *node = ovsrcu_get(struct trie_node *, trie);
|
|
|
|
|
|
|
|
|
|
if (node) {
|
|
|
|
|
assert(node->n_rules == 0 || node->n_bits > 0);
|
|
|
|
|
ofs += node->n_bits;
|
|
|
|
|
assert((ofs > 0 || (ofs == 0 && node->n_bits == 0)) && ofs <= n_bits);
|
|
|
|
|
|
|
|
|
|
return node->n_rules
|
|
|
|
|
+ trie_verify(&node->edges[0], ofs, n_bits)
|
|
|
|
|
+ trie_verify(&node->edges[1], ofs, n_bits);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-07-18 02:24:26 -07:00
|
|
|
|
verify_tries(struct classifier *cls)
|
2014-07-11 02:29:08 -07:00
|
|
|
|
{
|
|
|
|
|
unsigned int n_rules = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < cls->n_tries; i++) {
|
|
|
|
|
n_rules += trie_verify(&cls->tries[i].root, 0,
|
|
|
|
|
cls->tries[i].field->n_bits);
|
|
|
|
|
}
|
|
|
|
|
ovs_mutex_lock(&cls->mutex);
|
|
|
|
|
assert(n_rules <= cls->n_rules);
|
|
|
|
|
ovs_mutex_unlock(&cls->mutex);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2013-07-11 14:19:11 -07:00
|
|
|
|
check_tables(const struct classifier *cls, int n_tables, int n_rules,
|
lib/classifier: Lockless lookups.
Now that all the relevant classifier structures use RCU and internal
mutual exclusion for modifications, we can remove the fat-rwlock and
thus make the classifier lookups lockless.
As the readers are operating concurrently with the writers, a
concurrent reader may or may not see a new rule being added by a
writer, depending on how the concurrent events overlap with each
other. Overall, this is no different from the former locked behavior,
but there the visibility of the new rule only depended on the timing
of the locking functions.
A new rule is first added to the segment indices, so the readers may
find the rule in the indices before the rule is visible in the
subtables 'rules' map. This may result in us losing the opportunity
to quit lookups earlier, resulting in sub-optimal wildcarding. This
will be fixed by forthcoming revalidation always scheduled after flow
table changes.
Similar behavior may happen due to us removing the overlapping rule
(if any) from the indices only after the corresponding new rule has
been added.
The subtable's max priority is updated only after a rule is inserted
to the maps, so the concurrent readers may not see the rule, as the
updated priority ordered subtable list will only be visible after the
subtable's max priority is updated.
Similarly, the classifier's partitions are updated by the caller after
the rule is inserted to the maps, so the readers may keep skipping the
subtable until they see the updated partitions.
Signed-off-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
2014-07-11 02:29:08 -07:00
|
|
|
|
int n_dups)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2013-10-29 16:39:52 -07:00
|
|
|
|
const struct cls_subtable *table;
|
2010-11-19 16:41:02 -08:00
|
|
|
|
struct test_rule *test_rule;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int found_tables = 0;
|
|
|
|
|
int found_rules = 0;
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int found_dups = 0;
|
2010-11-19 16:41:02 -08:00
|
|
|
|
int found_rules2 = 0;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2014-07-18 02:24:26 -07:00
|
|
|
|
pvector_verify(&cls->subtables);
|
|
|
|
|
CMAP_FOR_EACH (table, cmap_node, &cls->subtables_map) {
|
2014-04-29 15:50:38 -07:00
|
|
|
|
const struct cls_match *head;
|
2013-02-08 00:06:22 +02:00
|
|
|
|
unsigned int max_priority = 0;
|
|
|
|
|
unsigned int max_count = 0;
|
2014-06-26 07:41:25 -07:00
|
|
|
|
bool found = false;
|
|
|
|
|
const struct cls_subtable *iter;
|
|
|
|
|
|
|
|
|
|
/* Locate the subtable from 'subtables'. */
|
2014-07-18 02:24:26 -07:00
|
|
|
|
PVECTOR_FOR_EACH (iter, &cls->subtables) {
|
2014-06-26 07:41:25 -07:00
|
|
|
|
if (iter == table) {
|
|
|
|
|
if (found) {
|
|
|
|
|
VLOG_ABORT("Subtable %p duplicated in 'subtables'.",
|
|
|
|
|
table);
|
|
|
|
|
}
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
VLOG_ABORT("Subtable %p not found from 'subtables'.", table);
|
|
|
|
|
}
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
2014-07-11 02:29:07 -07:00
|
|
|
|
assert(!cmap_is_empty(&table->rules));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_lock(&cls->mutex);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
assert(trie_verify(&table->ports_trie, 0, table->ports_mask_len)
|
|
|
|
|
== (table->ports_mask_len ? table->n_rules : 0));
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_unlock(&cls->mutex);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
found_tables++;
|
2014-07-11 02:29:07 -07:00
|
|
|
|
CMAP_FOR_EACH (head, cmap_node, &table->rules) {
|
2010-11-03 11:00:58 -07:00
|
|
|
|
unsigned int prev_priority = UINT_MAX;
|
2014-04-29 15:50:38 -07:00
|
|
|
|
const struct cls_match *rule;
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
2013-02-08 00:06:22 +02:00
|
|
|
|
if (head->priority > max_priority) {
|
|
|
|
|
max_priority = head->priority;
|
|
|
|
|
max_count = 1;
|
|
|
|
|
} else if (head->priority == max_priority) {
|
|
|
|
|
++max_count;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
found_rules++;
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_lock(&cls->mutex);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
LIST_FOR_EACH (rule, list, &head->list) {
|
|
|
|
|
assert(rule->priority < prev_priority);
|
2013-02-08 00:06:22 +02:00
|
|
|
|
assert(rule->priority <= table->max_priority);
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
prev_priority = rule->priority;
|
|
|
|
|
found_rules++;
|
|
|
|
|
found_dups++;
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_unlock(&cls->mutex);
|
2014-04-29 15:50:38 -07:00
|
|
|
|
assert(classifier_find_rule_exactly(cls, rule->cls_rule)
|
|
|
|
|
== rule->cls_rule);
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_lock(&cls->mutex);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
}
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_unlock(&cls->mutex);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
}
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_lock(&cls->mutex);
|
2013-02-08 00:06:22 +02:00
|
|
|
|
assert(table->max_priority == max_priority);
|
|
|
|
|
assert(table->max_count == max_count);
|
2014-07-18 02:24:26 -07:00
|
|
|
|
ovs_mutex_unlock(&cls->mutex);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-18 02:24:26 -07:00
|
|
|
|
assert(found_tables == cmap_count(&cls->subtables_map));
|
|
|
|
|
assert(found_tables == pvector_count(&cls->subtables));
|
|
|
|
|
assert(n_tables == -1 || n_tables == cmap_count(&cls->subtables_map));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
assert(n_rules == -1 || found_rules == n_rules);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
assert(n_dups == -1 || found_dups == n_dups);
|
2010-11-19 16:41:02 -08:00
|
|
|
|
|
2014-07-11 02:29:07 -07:00
|
|
|
|
CLS_FOR_EACH (test_rule, cls_rule, cls) {
|
2010-11-19 16:41:02 -08:00
|
|
|
|
found_rules2++;
|
|
|
|
|
}
|
|
|
|
|
assert(found_rules == found_rules2);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct test_rule *
|
|
|
|
|
make_rule(int wc_fields, unsigned int priority, int value_pat)
|
|
|
|
|
{
|
|
|
|
|
const struct cls_field *f;
|
|
|
|
|
struct test_rule *rule;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
struct match match;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match_init_catchall(&match);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
for (f = &cls_fields[0]; f < &cls_fields[CLS_N_FIELDS]; f++) {
|
|
|
|
|
int f_idx = f - cls_fields;
|
2010-11-10 14:39:54 -08:00
|
|
|
|
int value_idx = (value_pat & (1u << f_idx)) != 0;
|
2012-08-07 15:28:18 -07:00
|
|
|
|
memcpy((char *) &match.flow + f->ofs,
|
2010-11-10 14:39:54 -08:00
|
|
|
|
values[f_idx][value_idx], f->len);
|
|
|
|
|
|
2012-08-03 13:27:15 -07:00
|
|
|
|
if (f_idx == CLS_F_IDX_NW_SRC) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.nw_src = OVS_BE32_MAX;
|
2010-11-10 14:39:54 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_NW_DST) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.nw_dst = OVS_BE32_MAX;
|
2012-01-27 17:16:05 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_TP_SRC) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.tp_src = OVS_BE16_MAX;
|
2012-01-27 17:16:05 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_TP_DST) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.tp_dst = OVS_BE16_MAX;
|
2012-05-29 00:38:21 +12:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_DL_SRC) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
memset(match.wc.masks.dl_src, 0xff, ETH_ADDR_LEN);
|
2012-05-29 00:38:21 +12:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_DL_DST) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
memset(match.wc.masks.dl_dst, 0xff, ETH_ADDR_LEN);
|
2010-11-23 10:06:28 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_VLAN_TCI) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.vlan_tci = OVS_BE16_MAX;
|
2011-01-20 15:29:00 -08:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_TUN_ID) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.tunnel.tun_id = OVS_BE64_MAX;
|
2012-07-12 00:01:11 +12:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_METADATA) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.metadata = OVS_BE64_MAX;
|
2012-06-18 14:11:13 -07:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_NW_DSCP) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match.wc.masks.nw_tos |= IP_DSCP_MASK;
|
2012-06-18 14:12:52 -07:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_NW_PROTO) {
|
2012-08-07 15:28:18 -07:00
|
|
|
|
match.wc.masks.nw_proto = UINT8_MAX;
|
2012-06-18 13:33:13 -07:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_DL_TYPE) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
match.wc.masks.dl_type = OVS_BE16_MAX;
|
2012-08-03 13:27:15 -07:00
|
|
|
|
} else if (f_idx == CLS_F_IDX_IN_PORT) {
|
2013-06-20 15:10:04 -07:00
|
|
|
|
match.wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
} else {
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-07 15:28:18 -07:00
|
|
|
|
|
|
|
|
|
rule = xzalloc(sizeof *rule);
|
|
|
|
|
cls_rule_init(&rule->cls_rule, &match, wc_fields ? priority : UINT_MAX);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
return rule;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-20 11:29:43 -07:00
|
|
|
|
static struct test_rule *
|
|
|
|
|
clone_rule(const struct test_rule *src)
|
|
|
|
|
{
|
|
|
|
|
struct test_rule *dst;
|
|
|
|
|
|
|
|
|
|
dst = xmalloc(sizeof *dst);
|
|
|
|
|
dst->aux = src->aux;
|
|
|
|
|
cls_rule_clone(&dst->cls_rule, &src->cls_rule);
|
|
|
|
|
return dst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
free_rule(struct test_rule *rule)
|
|
|
|
|
{
|
|
|
|
|
cls_rule_destroy(&rule->cls_rule);
|
|
|
|
|
free(rule);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
|
|
|
|
shuffle(unsigned int *p, size_t n)
|
|
|
|
|
{
|
|
|
|
|
for (; n > 1; n--, p++) {
|
2013-06-25 09:22:11 -07:00
|
|
|
|
unsigned int *q = &p[random_range(n)];
|
2009-07-08 13:19:16 -07:00
|
|
|
|
unsigned int tmp = *p;
|
|
|
|
|
*p = *q;
|
|
|
|
|
*q = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-09-04 12:43:53 -07:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
shuffle_u32s(uint32_t *p, size_t n)
|
|
|
|
|
{
|
|
|
|
|
for (; n > 1; n--, p++) {
|
2013-06-25 09:22:11 -07:00
|
|
|
|
uint32_t *q = &p[random_range(n)];
|
2012-09-04 12:43:53 -07:00
|
|
|
|
uint32_t tmp = *p;
|
|
|
|
|
*p = *q;
|
|
|
|
|
*q = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-09-04 12:43:53 -07:00
|
|
|
|
/* Classifier tests. */
|
|
|
|
|
|
2013-12-11 11:07:01 -08:00
|
|
|
|
static enum mf_field_id trie_fields[2] = {
|
|
|
|
|
MFF_IPV4_DST, MFF_IPV4_SRC
|
|
|
|
|
};
|
|
|
|
|
|
2014-07-11 02:29:08 -07:00
|
|
|
|
static void
|
|
|
|
|
set_prefix_fields(struct classifier *cls)
|
|
|
|
|
{
|
|
|
|
|
verify_tries(cls);
|
|
|
|
|
classifier_set_prefix_fields(cls, trie_fields, ARRAY_SIZE(trie_fields));
|
|
|
|
|
verify_tries(cls);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
/* Tests an empty classifier. */
|
|
|
|
|
static void
|
2010-05-26 12:48:32 -07:00
|
|
|
|
test_empty(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
struct classifier cls;
|
|
|
|
|
struct tcls tcls;
|
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, flow_segment_u32s);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
set_prefix_fields(&cls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
tcls_init(&tcls);
|
|
|
|
|
assert(classifier_is_empty(&cls));
|
|
|
|
|
assert(tcls_is_empty(&tcls));
|
|
|
|
|
compare_classifiers(&cls, &tcls);
|
|
|
|
|
classifier_destroy(&cls);
|
|
|
|
|
tcls_destroy(&tcls);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Destroys a null classifier. */
|
|
|
|
|
static void
|
2010-05-26 12:48:32 -07:00
|
|
|
|
test_destroy_null(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
classifier_destroy(NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tests classification with one rule at a time. */
|
|
|
|
|
static void
|
2010-05-26 12:48:32 -07:00
|
|
|
|
test_single_rule(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
unsigned int wc_fields; /* Hilarious. */
|
|
|
|
|
|
|
|
|
|
for (wc_fields = 0; wc_fields < (1u << CLS_N_FIELDS); wc_fields++) {
|
|
|
|
|
struct classifier cls;
|
|
|
|
|
struct test_rule *rule, *tcls_rule;
|
|
|
|
|
struct tcls tcls;
|
|
|
|
|
|
|
|
|
|
rule = make_rule(wc_fields,
|
|
|
|
|
hash_bytes(&wc_fields, sizeof wc_fields, 0), 0);
|
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, flow_segment_u32s);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
set_prefix_fields(&cls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
tcls_init(&tcls);
|
|
|
|
|
|
|
|
|
|
tcls_rule = tcls_insert(&tcls, rule);
|
2011-05-11 14:06:48 -07:00
|
|
|
|
classifier_insert(&cls, &rule->cls_rule);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
check_tables(&cls, 1, 1, 0);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
classifier_remove(&cls, &rule->cls_rule);
|
|
|
|
|
tcls_remove(&tcls, tcls_rule);
|
|
|
|
|
assert(classifier_is_empty(&cls));
|
|
|
|
|
assert(tcls_is_empty(&tcls));
|
|
|
|
|
compare_classifiers(&cls, &tcls);
|
|
|
|
|
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(rule);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
classifier_destroy(&cls);
|
|
|
|
|
tcls_destroy(&tcls);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tests replacing one rule by another. */
|
|
|
|
|
static void
|
2010-05-26 12:48:32 -07:00
|
|
|
|
test_rule_replacement(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
unsigned int wc_fields;
|
|
|
|
|
|
|
|
|
|
for (wc_fields = 0; wc_fields < (1u << CLS_N_FIELDS); wc_fields++) {
|
|
|
|
|
struct classifier cls;
|
2010-02-10 10:55:45 -08:00
|
|
|
|
struct test_rule *rule1;
|
|
|
|
|
struct test_rule *rule2;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
struct tcls tcls;
|
|
|
|
|
|
|
|
|
|
rule1 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX);
|
|
|
|
|
rule2 = make_rule(wc_fields, OFP_DEFAULT_PRIORITY, UINT_MAX);
|
|
|
|
|
rule2->aux += 5;
|
|
|
|
|
rule2->aux += 5;
|
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, flow_segment_u32s);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
set_prefix_fields(&cls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
tcls_init(&tcls);
|
2010-02-10 10:55:45 -08:00
|
|
|
|
tcls_insert(&tcls, rule1);
|
2011-05-11 14:06:48 -07:00
|
|
|
|
classifier_insert(&cls, &rule1->cls_rule);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
check_tables(&cls, 1, 1, 0);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
tcls_destroy(&tcls);
|
|
|
|
|
|
|
|
|
|
tcls_init(&tcls);
|
2010-02-10 10:55:45 -08:00
|
|
|
|
tcls_insert(&tcls, rule2);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
|
2009-07-08 13:19:16 -07:00
|
|
|
|
assert(test_rule_from_cls_rule(
|
2011-05-11 14:06:48 -07:00
|
|
|
|
classifier_replace(&cls, &rule2->cls_rule)) == rule1);
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(rule1);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
check_tables(&cls, 1, 1, 0);
|
|
|
|
|
|
|
|
|
|
tcls_destroy(&tcls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
destroy_classifier(&cls);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2010-11-03 11:00:58 -07:00
|
|
|
|
factorial(int n_items)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int n, i;
|
|
|
|
|
|
|
|
|
|
n = 1;
|
|
|
|
|
for (i = 2; i <= n_items; i++) {
|
|
|
|
|
n *= i;
|
|
|
|
|
}
|
|
|
|
|
return n;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
static void
|
|
|
|
|
swap(int *a, int *b)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int tmp = *a;
|
|
|
|
|
*a = *b;
|
|
|
|
|
*b = tmp;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2010-11-03 11:00:58 -07:00
|
|
|
|
reverse(int *a, int n)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int i;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (i = 0; i < n / 2; i++) {
|
|
|
|
|
int j = n - (i + 1);
|
|
|
|
|
swap(&a[i], &a[j]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
static bool
|
|
|
|
|
next_permutation(int *a, int n)
|
|
|
|
|
{
|
|
|
|
|
int k;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (k = n - 2; k >= 0; k--) {
|
|
|
|
|
if (a[k] < a[k + 1]) {
|
|
|
|
|
int l;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (l = n - 1; ; l--) {
|
|
|
|
|
if (a[l] > a[k]) {
|
|
|
|
|
swap(&a[k], &a[l]);
|
|
|
|
|
reverse(a + (k + 1), n - (k + 1));
|
|
|
|
|
return true;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-03 11:00:58 -07:00
|
|
|
|
return false;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
/* Tests classification with rules that have the same matching criteria. */
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-11-03 11:00:58 -07:00
|
|
|
|
test_many_rules_in_one_list (int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
enum { N_RULES = 3 };
|
|
|
|
|
int n_pris;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (n_pris = N_RULES; n_pris >= 1; n_pris--) {
|
|
|
|
|
int ops[N_RULES * 2];
|
|
|
|
|
int pris[N_RULES];
|
|
|
|
|
int n_permutations;
|
|
|
|
|
int i;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
pris[0] = 0;
|
|
|
|
|
for (i = 1; i < N_RULES; i++) {
|
|
|
|
|
pris[i] = pris[i - 1] + (n_pris > i);
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (i = 0; i < N_RULES * 2; i++) {
|
|
|
|
|
ops[i] = i / 2;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
n_permutations = 0;
|
|
|
|
|
do {
|
|
|
|
|
struct test_rule *rules[N_RULES];
|
|
|
|
|
struct test_rule *tcls_rules[N_RULES];
|
|
|
|
|
int pri_rules[N_RULES];
|
|
|
|
|
struct classifier cls;
|
|
|
|
|
struct tcls tcls;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
n_permutations++;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (i = 0; i < N_RULES; i++) {
|
|
|
|
|
rules[i] = make_rule(456, pris[i], 0);
|
|
|
|
|
tcls_rules[i] = NULL;
|
|
|
|
|
pri_rules[i] = -1;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, flow_segment_u32s);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
set_prefix_fields(&cls);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
tcls_init(&tcls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ops); i++) {
|
|
|
|
|
int j = ops[i];
|
|
|
|
|
int m, n;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
if (!tcls_rules[j]) {
|
|
|
|
|
struct test_rule *displaced_rule;
|
|
|
|
|
|
|
|
|
|
tcls_rules[j] = tcls_insert(&tcls, rules[j]);
|
|
|
|
|
displaced_rule = test_rule_from_cls_rule(
|
2011-05-11 14:06:48 -07:00
|
|
|
|
classifier_replace(&cls, &rules[j]->cls_rule));
|
2010-11-03 11:00:58 -07:00
|
|
|
|
if (pri_rules[pris[j]] >= 0) {
|
|
|
|
|
int k = pri_rules[pris[j]];
|
|
|
|
|
assert(displaced_rule != NULL);
|
|
|
|
|
assert(displaced_rule != rules[j]);
|
|
|
|
|
assert(pris[j] == displaced_rule->cls_rule.priority);
|
|
|
|
|
tcls_rules[k] = NULL;
|
|
|
|
|
} else {
|
|
|
|
|
assert(displaced_rule == NULL);
|
|
|
|
|
}
|
|
|
|
|
pri_rules[pris[j]] = j;
|
|
|
|
|
} else {
|
|
|
|
|
classifier_remove(&cls, &rules[j]->cls_rule);
|
|
|
|
|
tcls_remove(&tcls, tcls_rules[j]);
|
|
|
|
|
tcls_rules[j] = NULL;
|
|
|
|
|
pri_rules[pris[j]] = -1;
|
|
|
|
|
}
|
2014-07-11 02:29:07 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
n = 0;
|
|
|
|
|
for (m = 0; m < N_RULES; m++) {
|
|
|
|
|
n += tcls_rules[m] != NULL;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-11-03 11:00:58 -07:00
|
|
|
|
check_tables(&cls, n > 0, n, n - 1);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
|
|
|
|
for (i = 0; i < N_RULES; i++) {
|
2014-04-29 15:50:38 -07:00
|
|
|
|
if (rules[i]->cls_rule.cls_match) {
|
|
|
|
|
classifier_remove(&cls, &rules[i]->cls_rule);
|
|
|
|
|
}
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(rules[i]);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
}
|
2014-04-29 15:50:38 -07:00
|
|
|
|
classifier_destroy(&cls);
|
|
|
|
|
tcls_destroy(&tcls);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
} while (next_permutation(ops, ARRAY_SIZE(ops)));
|
|
|
|
|
assert(n_permutations == (factorial(N_RULES * 2) >> N_RULES));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
static int
|
|
|
|
|
count_ones(unsigned long int x)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int n = 0;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
while (x) {
|
2012-07-20 11:45:33 -07:00
|
|
|
|
x = zero_rightmost_1bit(x);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
n++;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
return n;
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
static bool
|
|
|
|
|
array_contains(int *array, int n, int value)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
if (array[i] == value) {
|
|
|
|
|
return true;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
|
|
|
|
return false;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
/* Tests classification with two rules at a time that fall into the same
|
|
|
|
|
* table but different lists. */
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-05-26 12:48:32 -07:00
|
|
|
|
test_many_rules_in_one_table(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int iteration;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (iteration = 0; iteration < 50; iteration++) {
|
|
|
|
|
enum { N_RULES = 20 };
|
|
|
|
|
struct test_rule *rules[N_RULES];
|
|
|
|
|
struct test_rule *tcls_rules[N_RULES];
|
|
|
|
|
struct classifier cls;
|
|
|
|
|
struct tcls tcls;
|
|
|
|
|
int value_pats[N_RULES];
|
|
|
|
|
int value_mask;
|
|
|
|
|
int wcf;
|
|
|
|
|
int i;
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
do {
|
2013-06-25 09:22:11 -07:00
|
|
|
|
wcf = random_uint32() & ((1u << CLS_N_FIELDS) - 1);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
value_mask = ~wcf & ((1u << CLS_N_FIELDS) - 1);
|
|
|
|
|
} while ((1 << count_ones(value_mask)) < N_RULES);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, flow_segment_u32s);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
set_prefix_fields(&cls);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
tcls_init(&tcls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
for (i = 0; i < N_RULES; i++) {
|
2013-06-25 09:22:11 -07:00
|
|
|
|
unsigned int priority = random_uint32();
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
do {
|
2013-06-25 09:22:11 -07:00
|
|
|
|
value_pats[i] = random_uint32() & value_mask;
|
2010-11-03 11:00:58 -07:00
|
|
|
|
} while (array_contains(value_pats, i, value_pats[i]));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
rules[i] = make_rule(wcf, priority, value_pats[i]);
|
|
|
|
|
tcls_rules[i] = tcls_insert(&tcls, rules[i]);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
|
2011-05-11 14:06:48 -07:00
|
|
|
|
classifier_insert(&cls, &rules[i]->cls_rule);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
|
|
|
|
check_tables(&cls, 1, i + 1, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < N_RULES; i++) {
|
|
|
|
|
tcls_remove(&tcls, tcls_rules[i]);
|
|
|
|
|
classifier_remove(&cls, &rules[i]->cls_rule);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(rules[i]);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
|
|
|
|
check_tables(&cls, i < N_RULES - 1, N_RULES - (i + 1), 0);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
|
|
|
|
classifier_destroy(&cls);
|
|
|
|
|
tcls_destroy(&tcls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-03 11:00:58 -07:00
|
|
|
|
/* Tests classification with many rules at a time that fall into random lists
|
|
|
|
|
* in 'n' tables. */
|
2009-07-08 13:19:16 -07:00
|
|
|
|
static void
|
2010-11-03 11:00:58 -07:00
|
|
|
|
test_many_rules_in_n_tables(int n_tables)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
|
|
|
|
enum { MAX_RULES = 50 };
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int wcfs[10];
|
2009-07-08 13:19:16 -07:00
|
|
|
|
int iteration;
|
2010-11-03 11:00:58 -07:00
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
assert(n_tables < 10);
|
|
|
|
|
for (i = 0; i < n_tables; i++) {
|
|
|
|
|
do {
|
2013-06-25 09:22:11 -07:00
|
|
|
|
wcfs[i] = random_uint32() & ((1u << CLS_N_FIELDS) - 1);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
} while (array_contains(wcfs, i, wcfs[i]));
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
|
|
|
|
for (iteration = 0; iteration < 30; iteration++) {
|
|
|
|
|
unsigned int priorities[MAX_RULES];
|
|
|
|
|
struct classifier cls;
|
|
|
|
|
struct tcls tcls;
|
|
|
|
|
|
2013-06-25 09:22:11 -07:00
|
|
|
|
random_set_seed(iteration + 1);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
for (i = 0; i < MAX_RULES; i++) {
|
|
|
|
|
priorities[i] = i * 129;
|
|
|
|
|
}
|
|
|
|
|
shuffle(priorities, ARRAY_SIZE(priorities));
|
|
|
|
|
|
2013-11-19 17:31:29 -08:00
|
|
|
|
classifier_init(&cls, flow_segment_u32s);
|
2014-07-11 02:29:08 -07:00
|
|
|
|
set_prefix_fields(&cls);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
tcls_init(&tcls);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_RULES; i++) {
|
|
|
|
|
struct test_rule *rule;
|
|
|
|
|
unsigned int priority = priorities[i];
|
2013-06-25 09:22:11 -07:00
|
|
|
|
int wcf = wcfs[random_range(n_tables)];
|
|
|
|
|
int value_pat = random_uint32() & ((1u << CLS_N_FIELDS) - 1);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
rule = make_rule(wcf, priority, value_pat);
|
|
|
|
|
tcls_insert(&tcls, rule);
|
2011-05-11 14:06:48 -07:00
|
|
|
|
classifier_insert(&cls, &rule->cls_rule);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2014-07-11 02:29:07 -07:00
|
|
|
|
check_tables(&cls, -1, i + 1, -1);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!classifier_is_empty(&cls)) {
|
2010-10-28 16:18:20 -07:00
|
|
|
|
struct test_rule *target;
|
2014-07-21 21:00:04 -07:00
|
|
|
|
struct test_rule *rule;
|
2010-10-28 16:18:20 -07:00
|
|
|
|
|
2013-06-25 09:22:11 -07:00
|
|
|
|
target = clone_rule(tcls.rules[random_range(tcls.n_rules)]);
|
2010-10-28 16:18:20 -07:00
|
|
|
|
|
2014-07-21 21:00:04 -07:00
|
|
|
|
CLS_FOR_EACH_TARGET_SAFE (rule, cls_rule, &cls,
|
2014-07-11 02:29:07 -07:00
|
|
|
|
&target->cls_rule) {
|
2010-10-28 16:18:20 -07:00
|
|
|
|
classifier_remove(&cls, &rule->cls_rule);
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(rule);
|
2010-10-28 16:18:20 -07:00
|
|
|
|
}
|
2014-07-11 02:29:07 -07:00
|
|
|
|
|
2010-10-28 16:18:20 -07:00
|
|
|
|
tcls_delete_matches(&tcls, &target->cls_rule);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
compare_classifiers(&cls, &tcls);
|
2010-11-03 11:00:58 -07:00
|
|
|
|
check_tables(&cls, -1, -1, -1);
|
2012-08-20 11:29:43 -07:00
|
|
|
|
free_rule(target);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
destroy_classifier(&cls);
|
|
|
|
|
tcls_destroy(&tcls);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-03 11:00:58 -07:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_many_rules_in_two_tables(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
test_many_rules_in_n_tables(2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_many_rules_in_five_tables(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
test_many_rules_in_n_tables(5);
|
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2012-09-04 12:43:53 -07:00
|
|
|
|
/* Miniflow tests. */
|
|
|
|
|
|
|
|
|
|
static uint32_t
|
|
|
|
|
random_value(void)
|
|
|
|
|
{
|
|
|
|
|
static const uint32_t values[] =
|
|
|
|
|
{ 0xffffffff, 0xaaaaaaaa, 0x55555555, 0x80000000,
|
|
|
|
|
0x00000001, 0xface0000, 0x00d00d1e, 0xdeadbeef };
|
|
|
|
|
|
2013-06-25 09:22:11 -07:00
|
|
|
|
return values[random_range(ARRAY_SIZE(values))];
|
2012-09-04 12:43:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
choose(unsigned int n, unsigned int *idxp)
|
|
|
|
|
{
|
|
|
|
|
if (*idxp < n) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
*idxp -= n;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
init_consecutive_values(int n_consecutive, struct flow *flow,
|
|
|
|
|
unsigned int *idxp)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *flow_u32 = (uint32_t *) flow;
|
|
|
|
|
|
|
|
|
|
if (choose(FLOW_U32S - n_consecutive + 1, idxp)) {
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_consecutive; i++) {
|
|
|
|
|
flow_u32[*idxp + i] = random_value();
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
next_random_flow(struct flow *flow, unsigned int idx)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *flow_u32 = (uint32_t *) flow;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
memset(flow, 0, sizeof *flow);
|
|
|
|
|
|
|
|
|
|
/* Empty flow. */
|
|
|
|
|
if (choose(1, &idx)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* All flows with a small number of consecutive nonzero values. */
|
|
|
|
|
for (i = 1; i <= 4; i++) {
|
|
|
|
|
if (init_consecutive_values(i, flow, &idx)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* All flows with a large number of consecutive nonzero values. */
|
|
|
|
|
for (i = FLOW_U32S - 4; i <= FLOW_U32S; i++) {
|
|
|
|
|
if (init_consecutive_values(i, flow, &idx)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* All flows with exactly two nonconsecutive nonzero values. */
|
|
|
|
|
if (choose((FLOW_U32S - 1) * (FLOW_U32S - 2) / 2, &idx)) {
|
|
|
|
|
int ofs1;
|
|
|
|
|
|
|
|
|
|
for (ofs1 = 0; ofs1 < FLOW_U32S - 2; ofs1++) {
|
|
|
|
|
int ofs2;
|
|
|
|
|
|
|
|
|
|
for (ofs2 = ofs1 + 2; ofs2 < FLOW_U32S; ofs2++) {
|
|
|
|
|
if (choose(1, &idx)) {
|
|
|
|
|
flow_u32[ofs1] = random_value();
|
|
|
|
|
flow_u32[ofs2] = random_value();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-17 10:32:12 -08:00
|
|
|
|
OVS_NOT_REACHED();
|
2012-09-04 12:43:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 16 randomly chosen flows with N >= 3 nonzero values. */
|
|
|
|
|
if (choose(16 * (FLOW_U32S - 4), &idx)) {
|
|
|
|
|
int n = idx / 16 + 3;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
flow_u32[i] = random_value();
|
|
|
|
|
}
|
|
|
|
|
shuffle_u32s(flow_u32, FLOW_U32S);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
any_random_flow(struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
static unsigned int max;
|
|
|
|
|
if (!max) {
|
|
|
|
|
while (next_random_flow(flow, max)) {
|
|
|
|
|
max++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_random_flow(flow, random_range(max));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
toggle_masked_flow_bits(struct flow *flow, const struct flow_wildcards *mask)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t *mask_u32 = (const uint32_t *) &mask->masks;
|
|
|
|
|
uint32_t *flow_u32 = (uint32_t *) flow;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < FLOW_U32S; i++) {
|
|
|
|
|
if (mask_u32[i] != 0) {
|
|
|
|
|
uint32_t bit;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
bit = 1u << random_range(32);
|
|
|
|
|
} while (!(bit & mask_u32[i]));
|
|
|
|
|
flow_u32[i] ^= bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wildcard_extra_bits(struct flow_wildcards *mask)
|
|
|
|
|
{
|
|
|
|
|
uint32_t *mask_u32 = (uint32_t *) &mask->masks;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < FLOW_U32S; i++) {
|
|
|
|
|
if (mask_u32[i] != 0) {
|
|
|
|
|
uint32_t bit;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
bit = 1u << random_range(32);
|
|
|
|
|
} while (!(bit & mask_u32[i]));
|
|
|
|
|
mask_u32[i] &= ~bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_miniflow(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
struct flow flow;
|
|
|
|
|
unsigned int idx;
|
|
|
|
|
|
|
|
|
|
random_set_seed(0xb3faca38);
|
|
|
|
|
for (idx = 0; next_random_flow(&flow, idx); idx++) {
|
|
|
|
|
const uint32_t *flow_u32 = (const uint32_t *) &flow;
|
|
|
|
|
struct miniflow miniflow, miniflow2, miniflow3;
|
|
|
|
|
struct flow flow2, flow3;
|
|
|
|
|
struct flow_wildcards mask;
|
|
|
|
|
struct minimask minimask;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Convert flow to miniflow. */
|
|
|
|
|
miniflow_init(&miniflow, &flow);
|
|
|
|
|
|
|
|
|
|
/* Check that the flow equals its miniflow. */
|
|
|
|
|
assert(miniflow_get_vid(&miniflow) == vlan_tci_to_vid(flow.vlan_tci));
|
|
|
|
|
for (i = 0; i < FLOW_U32S; i++) {
|
2014-04-29 15:50:38 -07:00
|
|
|
|
assert(MINIFLOW_GET_TYPE(&miniflow, uint32_t, i * 4)
|
|
|
|
|
== flow_u32[i]);
|
2012-09-04 12:43:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check that the miniflow equals itself. */
|
|
|
|
|
assert(miniflow_equal(&miniflow, &miniflow));
|
|
|
|
|
|
|
|
|
|
/* Convert miniflow back to flow and verify that it's the same. */
|
|
|
|
|
miniflow_expand(&miniflow, &flow2);
|
|
|
|
|
assert(flow_equal(&flow, &flow2));
|
|
|
|
|
|
|
|
|
|
/* Check that copying a miniflow works properly. */
|
|
|
|
|
miniflow_clone(&miniflow2, &miniflow);
|
|
|
|
|
assert(miniflow_equal(&miniflow, &miniflow2));
|
|
|
|
|
assert(miniflow_hash(&miniflow, 0) == miniflow_hash(&miniflow2, 0));
|
|
|
|
|
miniflow_expand(&miniflow2, &flow3);
|
|
|
|
|
assert(flow_equal(&flow, &flow3));
|
|
|
|
|
|
|
|
|
|
/* Check that masked matches work as expected for identical flows and
|
|
|
|
|
* miniflows. */
|
|
|
|
|
do {
|
|
|
|
|
next_random_flow(&mask.masks, 1);
|
|
|
|
|
} while (flow_wildcards_is_catchall(&mask));
|
|
|
|
|
minimask_init(&minimask, &mask);
|
|
|
|
|
assert(minimask_is_catchall(&minimask)
|
|
|
|
|
== flow_wildcards_is_catchall(&mask));
|
|
|
|
|
assert(miniflow_equal_in_minimask(&miniflow, &miniflow2, &minimask));
|
|
|
|
|
assert(miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
|
|
|
|
|
assert(miniflow_hash_in_minimask(&miniflow, &minimask, 0x12345678) ==
|
|
|
|
|
flow_hash_in_minimask(&flow, &minimask, 0x12345678));
|
|
|
|
|
|
|
|
|
|
/* Check that masked matches work as expected for differing flows and
|
|
|
|
|
* miniflows. */
|
|
|
|
|
toggle_masked_flow_bits(&flow2, &mask);
|
|
|
|
|
assert(!miniflow_equal_flow_in_minimask(&miniflow, &flow2, &minimask));
|
|
|
|
|
miniflow_init(&miniflow3, &flow2);
|
|
|
|
|
assert(!miniflow_equal_in_minimask(&miniflow, &miniflow3, &minimask));
|
|
|
|
|
|
|
|
|
|
/* Clean up. */
|
|
|
|
|
miniflow_destroy(&miniflow);
|
|
|
|
|
miniflow_destroy(&miniflow2);
|
|
|
|
|
miniflow_destroy(&miniflow3);
|
|
|
|
|
minimask_destroy(&minimask);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_minimask_has_extra(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
struct flow_wildcards catchall;
|
|
|
|
|
struct minimask minicatchall;
|
|
|
|
|
struct flow flow;
|
|
|
|
|
unsigned int idx;
|
|
|
|
|
|
|
|
|
|
flow_wildcards_init_catchall(&catchall);
|
|
|
|
|
minimask_init(&minicatchall, &catchall);
|
|
|
|
|
assert(minimask_is_catchall(&minicatchall));
|
|
|
|
|
|
|
|
|
|
random_set_seed(0x2ec7905b);
|
|
|
|
|
for (idx = 0; next_random_flow(&flow, idx); idx++) {
|
|
|
|
|
struct flow_wildcards mask;
|
|
|
|
|
struct minimask minimask;
|
|
|
|
|
|
|
|
|
|
mask.masks = flow;
|
|
|
|
|
minimask_init(&minimask, &mask);
|
|
|
|
|
assert(!minimask_has_extra(&minimask, &minimask));
|
|
|
|
|
assert(minimask_has_extra(&minicatchall, &minimask)
|
|
|
|
|
== !minimask_is_catchall(&minimask));
|
|
|
|
|
if (!minimask_is_catchall(&minimask)) {
|
|
|
|
|
struct minimask minimask2;
|
|
|
|
|
|
|
|
|
|
wildcard_extra_bits(&mask);
|
|
|
|
|
minimask_init(&minimask2, &mask);
|
|
|
|
|
assert(minimask_has_extra(&minimask2, &minimask));
|
|
|
|
|
assert(!minimask_has_extra(&minimask, &minimask2));
|
|
|
|
|
minimask_destroy(&minimask2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
minimask_destroy(&minimask);
|
|
|
|
|
}
|
2012-12-21 14:59:35 -08:00
|
|
|
|
|
|
|
|
|
minimask_destroy(&minicatchall);
|
2012-09-04 12:43:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
test_minimask_combine(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
struct flow_wildcards catchall;
|
|
|
|
|
struct minimask minicatchall;
|
|
|
|
|
struct flow flow;
|
|
|
|
|
unsigned int idx;
|
|
|
|
|
|
|
|
|
|
flow_wildcards_init_catchall(&catchall);
|
|
|
|
|
minimask_init(&minicatchall, &catchall);
|
|
|
|
|
assert(minimask_is_catchall(&minicatchall));
|
|
|
|
|
|
|
|
|
|
random_set_seed(0x181bf0cd);
|
|
|
|
|
for (idx = 0; next_random_flow(&flow, idx); idx++) {
|
|
|
|
|
struct minimask minimask, minimask2, minicombined;
|
|
|
|
|
struct flow_wildcards mask, mask2, combined, combined2;
|
|
|
|
|
uint32_t storage[FLOW_U32S];
|
|
|
|
|
struct flow flow2;
|
|
|
|
|
|
|
|
|
|
mask.masks = flow;
|
|
|
|
|
minimask_init(&minimask, &mask);
|
|
|
|
|
|
|
|
|
|
minimask_combine(&minicombined, &minimask, &minicatchall, storage);
|
|
|
|
|
assert(minimask_is_catchall(&minicombined));
|
|
|
|
|
|
|
|
|
|
any_random_flow(&flow2);
|
|
|
|
|
mask2.masks = flow2;
|
|
|
|
|
minimask_init(&minimask2, &mask2);
|
|
|
|
|
|
|
|
|
|
minimask_combine(&minicombined, &minimask, &minimask2, storage);
|
2013-06-10 22:48:58 -07:00
|
|
|
|
flow_wildcards_and(&combined, &mask, &mask2);
|
2012-09-04 12:43:53 -07:00
|
|
|
|
minimask_expand(&minicombined, &combined2);
|
|
|
|
|
assert(flow_wildcards_equal(&combined, &combined2));
|
|
|
|
|
|
|
|
|
|
minimask_destroy(&minimask);
|
|
|
|
|
minimask_destroy(&minimask2);
|
|
|
|
|
}
|
2012-12-21 14:59:35 -08:00
|
|
|
|
|
|
|
|
|
minimask_destroy(&minicatchall);
|
2012-09-04 12:43:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-05-26 12:48:32 -07:00
|
|
|
|
static const struct command commands[] = {
|
2012-09-04 12:43:53 -07:00
|
|
|
|
/* Classifier tests. */
|
2010-05-26 12:48:32 -07:00
|
|
|
|
{"empty", 0, 0, test_empty},
|
|
|
|
|
{"destroy-null", 0, 0, test_destroy_null},
|
|
|
|
|
{"single-rule", 0, 0, test_single_rule},
|
|
|
|
|
{"rule-replacement", 0, 0, test_rule_replacement},
|
2010-11-03 11:00:58 -07:00
|
|
|
|
{"many-rules-in-one-list", 0, 0, test_many_rules_in_one_list},
|
2010-05-26 12:48:32 -07:00
|
|
|
|
{"many-rules-in-one-table", 0, 0, test_many_rules_in_one_table},
|
2010-11-03 11:00:58 -07:00
|
|
|
|
{"many-rules-in-two-tables", 0, 0, test_many_rules_in_two_tables},
|
|
|
|
|
{"many-rules-in-five-tables", 0, 0, test_many_rules_in_five_tables},
|
2012-09-04 12:43:53 -07:00
|
|
|
|
|
|
|
|
|
/* Miniflow and minimask tests. */
|
|
|
|
|
{"miniflow", 0, 0, test_miniflow},
|
2014-04-01 00:47:01 -07:00
|
|
|
|
{"minimask_has_extra", 0, 0, test_minimask_has_extra},
|
|
|
|
|
{"minimask_combine", 0, 0, test_minimask_combine},
|
2012-09-04 12:43:53 -07:00
|
|
|
|
|
2010-05-26 12:48:32 -07:00
|
|
|
|
{NULL, 0, 0, NULL},
|
|
|
|
|
};
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2014-04-01 00:47:01 -07:00
|
|
|
|
static void
|
|
|
|
|
test_classifier_main(int argc, char *argv[])
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-11-03 11:00:58 -07:00
|
|
|
|
set_program_name(argv[0]);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
init_values();
|
2010-05-26 12:48:32 -07:00
|
|
|
|
run_command(argc - 1, argv + 1, commands);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2014-04-01 00:47:01 -07:00
|
|
|
|
|
|
|
|
|
OVSTEST_REGISTER("test-classifier", test_classifier_main);
|