diff options
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | lib/bond.c | 20 | ||||
-rw-r--r-- | lib/bond.h | 5 | ||||
-rw-r--r-- | lib/bundle.c | 19 | ||||
-rw-r--r-- | lib/bundle.h | 2 | ||||
-rw-r--r-- | lib/flow.c | 26 | ||||
-rw-r--r-- | lib/flow.h | 1 | ||||
-rw-r--r-- | lib/learn.c | 16 | ||||
-rw-r--r-- | lib/learn.h | 2 | ||||
-rw-r--r-- | lib/multipath.c | 7 | ||||
-rw-r--r-- | lib/multipath.h | 4 | ||||
-rw-r--r-- | lib/nx-match.c | 13 | ||||
-rw-r--r-- | lib/nx-match.h | 6 | ||||
-rw-r--r-- | ofproto/ofproto-dpif.c | 546 | ||||
-rw-r--r-- | tests/ofproto-dpif.at | 344 | ||||
-rw-r--r-- | tests/test-bundle.c | 4 | ||||
-rw-r--r-- | tests/test-multipath.c | 5 |
17 files changed, 855 insertions, 167 deletions
@@ -28,6 +28,8 @@ v1.11.0 - xx xxx xxxx in_port to some unused value, such as OFPP_NONE.) - ovs-dpctl: * New debugging commands "add-flow", "mod-flow", "del-flow". + - In dpif-based bridges, cache action translations, which can improve + flow set up performance by 80% with a complicated flow table. - New syslog format, prefixed with "ovs|", to be easier to filter. - RHEL: Removes the default firewall rule that allowed GRE traffic to pass through. Any users that relied on this automatic firewall hole diff --git a/lib/bond.c b/lib/bond.c index aca18a2d8..68ac06889 100644 --- a/lib/bond.c +++ b/lib/bond.c @@ -128,6 +128,7 @@ static struct bond_entry *lookup_bond_entry(const struct bond *, static tag_type bond_get_active_slave_tag(const struct bond *); static struct bond_slave *choose_output_slave(const struct bond *, const struct flow *, + struct flow_wildcards *, uint16_t vlan, tag_type *tags); static void bond_update_fake_slave_stats(struct bond *); @@ -505,7 +506,7 @@ bond_compose_learning_packet(struct bond *bond, memset(&flow, 0, sizeof flow); memcpy(flow.dl_src, eth_src, ETH_ADDR_LEN); - slave = choose_output_slave(bond, &flow, vlan, &tags); + slave = choose_output_slave(bond, &flow, NULL, vlan, &tags); packet = ofpbuf_new(0); compose_rarp(packet, eth_src); @@ -605,12 +606,17 @@ bond_check_admissibility(struct bond *bond, const void *slave_, * packet belongs to (so for an access port it will be the access port's VLAN). * * Adds a tag to '*tags' that associates the flow with the returned slave. + * + * If 'wc' is non-NULL, bitwise-OR's 'wc' with the set of bits that were + * significant in the selection. At some point earlier, 'wc' should + * have been initialized (e.g., by flow_wildcards_init_catchall()). */ void * bond_choose_output_slave(struct bond *bond, const struct flow *flow, - uint16_t vlan, tag_type *tags) + struct flow_wildcards *wc, uint16_t vlan, + tag_type *tags) { - struct bond_slave *slave = choose_output_slave(bond, flow, vlan, tags); + struct bond_slave *slave = choose_output_slave(bond, flow, wc, vlan, tags); if (slave) { *tags |= slave->tag; return slave->aux; @@ -1349,7 +1355,7 @@ lookup_bond_entry(const struct bond *bond, const struct flow *flow, static struct bond_slave * choose_output_slave(const struct bond *bond, const struct flow *flow, - uint16_t vlan, tag_type *tags) + struct flow_wildcards *wc, uint16_t vlan, tag_type *tags) { struct bond_entry *e; @@ -1368,8 +1374,14 @@ choose_output_slave(const struct bond *bond, const struct flow *flow, /* Must have LACP negotiations for TCP balanced bonds. */ return NULL; } + if (wc) { + flow_mask_hash_fields(wc, NX_HASH_FIELDS_SYMMETRIC_L4); + } /* Fall Through. */ case BM_SLB: + if (wc) { + flow_mask_hash_fields(wc, NX_HASH_FIELDS_ETH_SRC); + } e = lookup_bond_entry(bond, flow, vlan); if (!e->slave || !e->slave->enabled) { e->slave = CONTAINER_OF(hmap_random_node(&bond->slaves), diff --git a/lib/bond.h b/lib/bond.h index 619008126..306cf42b7 100644 --- a/lib/bond.h +++ b/lib/bond.h @@ -88,8 +88,9 @@ enum bond_verdict { enum bond_verdict bond_check_admissibility(struct bond *, const void *slave_, const uint8_t eth_dst[ETH_ADDR_LEN], tag_type *); -void *bond_choose_output_slave(struct bond *, - const struct flow *, uint16_t vlan, tag_type *); +void *bond_choose_output_slave(struct bond *, const struct flow *, + struct flow_wildcards *, uint16_t vlan, + tag_type *); /* Rebalancing. */ void bond_account(struct bond *, const struct flow *, uint16_t vlan, diff --git a/lib/bundle.c b/lib/bundle.c index 92ac1e1d4..b3821e85b 100644 --- a/lib/bundle.c +++ b/lib/bundle.c @@ -52,12 +52,17 @@ execute_ab(const struct ofpact_bundle *bundle, } static uint16_t -execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow, +execute_hrw(const struct ofpact_bundle *bundle, + const struct flow *flow, struct flow_wildcards *wc, bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux) { uint32_t flow_hash, best_hash; int best, i; + if (bundle->n_slaves > 1) { + flow_mask_hash_fields(wc, bundle->fields); + } + flow_hash = flow_hash_fields(flow, bundle->fields, bundle->basis); best = -1; best_hash = 0; @@ -76,16 +81,18 @@ execute_hrw(const struct ofpact_bundle *bundle, const struct flow *flow, return best >= 0 ? bundle->slaves[best] : OFPP_NONE; } -/* Executes 'bundle' on 'flow'. Uses 'slave_enabled' to determine if the slave - * designated by 'ofp_port' is up. Returns the chosen slave, or OFPP_NONE if - * none of the slaves are acceptable. */ +/* Executes 'bundle' on 'flow'. Sets fields in 'wc' that were used to + * calculate the result. Uses 'slave_enabled' to determine if the slave + * designated by 'ofp_port' is up. Returns the chosen slave, or + * OFPP_NONE if none of the slaves are acceptable. */ uint16_t -bundle_execute(const struct ofpact_bundle *bundle, const struct flow *flow, +bundle_execute(const struct ofpact_bundle *bundle, + const struct flow *flow, struct flow_wildcards *wc, bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux) { switch (bundle->algorithm) { case NX_BD_ALG_HRW: - return execute_hrw(bundle, flow, slave_enabled, aux); + return execute_hrw(bundle, flow, wc, slave_enabled, aux); case NX_BD_ALG_ACTIVE_BACKUP: return execute_ab(bundle, slave_enabled, aux); diff --git a/lib/bundle.h b/lib/bundle.h index 5b6bb6744..2619aebad 100644 --- a/lib/bundle.h +++ b/lib/bundle.h @@ -27,6 +27,7 @@ struct ds; struct flow; +struct flow_wildcards; struct ofpact_bundle; struct ofpbuf; @@ -35,6 +36,7 @@ struct ofpbuf; * See include/openflow/nicira-ext.h for NXAST_BUNDLE specification. */ uint16_t bundle_execute(const struct ofpact_bundle *, const struct flow *, + struct flow_wildcards *wc, bool (*slave_enabled)(uint16_t ofp_port, void *aux), void *aux); enum ofperr bundle_from_openflow(const struct nx_action_bundle *, diff --git a/lib/flow.c b/lib/flow.c index bf89fbc00..d38e3ab35 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -777,6 +777,32 @@ flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis) return jhash_bytes(&fields, sizeof fields, basis); } +/* Masks the fields in 'wc' that are used by the flow hash 'fields'. */ +void +flow_mask_hash_fields(struct flow_wildcards *wc, enum nx_hash_fields fields) +{ + switch (fields) { + case NX_HASH_FIELDS_ETH_SRC: + memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src); + break; + + case NX_HASH_FIELDS_SYMMETRIC_L4: + memset(&wc->masks.dl_src, 0xff, sizeof wc->masks.dl_src); + memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); + memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type); + memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); + memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); + memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); + memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); + memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src); + memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst); + break; + + default: + NOT_REACHED(); + } +} + /* Hashes the portions of 'flow' designated by 'fields'. */ uint32_t flow_hash_fields(const struct flow *flow, enum nx_hash_fields fields, diff --git a/lib/flow.h b/lib/flow.h index ff18dc640..b07b9ed20 100644 --- a/lib/flow.h +++ b/lib/flow.h @@ -207,6 +207,7 @@ bool flow_wildcards_equal(const struct flow_wildcards *, const struct flow_wildcards *); uint32_t flow_hash_symmetric_l4(const struct flow *flow, uint32_t basis); +void flow_mask_hash_fields(struct flow_wildcards *, enum nx_hash_fields); uint32_t flow_hash_fields(const struct flow *, enum nx_hash_fields, uint16_t basis); const char *flow_hash_fields_to_str(enum nx_hash_fields); diff --git a/lib/learn.c b/lib/learn.c index ab403bee5..606ea9ed0 100644 --- a/lib/learn.c +++ b/lib/learn.c @@ -374,6 +374,22 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow, fm->ofpacts_len = ofpacts->size; } +/* Perform a bitwise-OR on 'wc''s fields that are relevant as sources in + * the learn action 'learn'. */ +void +learn_mask(const struct ofpact_learn *learn, struct flow_wildcards *wc) +{ + const struct ofpact_learn_spec *spec; + union mf_subvalue value; + + memset(&value, 0xff, sizeof value); + for (spec = learn->specs; spec < &learn->specs[learn->n_specs]; spec++) { + if (spec->src_type == NX_LEARN_SRC_FIELD) { + mf_write_subfield_flow(&spec->src, &value, &wc->masks); + } + } +} + static void learn_parse_load_immediate(const char *s, struct ofpact_learn_spec *spec) { diff --git a/lib/learn.h b/lib/learn.h index 8ea8dcca6..5bceb6e50 100644 --- a/lib/learn.h +++ b/lib/learn.h @@ -21,6 +21,7 @@ struct ds; struct flow; +struct flow_wildcards; struct ofpbuf; struct ofpact_learn; struct ofputil_flow_mod; @@ -38,6 +39,7 @@ void learn_to_nxast(const struct ofpact_learn *, struct ofpbuf *openflow); void learn_execute(const struct ofpact_learn *, const struct flow *, struct ofputil_flow_mod *, struct ofpbuf *ofpacts); +void learn_mask(const struct ofpact_learn *, struct flow_wildcards *); void learn_parse(char *, struct ofpbuf *ofpacts); void learn_format(const struct ofpact_learn *, struct ds *); diff --git a/lib/multipath.c b/lib/multipath.c index f6a1a0aee..dbd570446 100644 --- a/lib/multipath.c +++ b/lib/multipath.c @@ -102,15 +102,18 @@ static uint16_t multipath_algorithm(uint32_t hash, enum nx_mp_algorithm, unsigned int n_links, unsigned int arg); /* Executes 'mp' based on the current contents of 'flow', writing the results - * back into 'flow'. */ + * back into 'flow'. Sets fields in 'wc' that were used to calculate + * the result. */ void -multipath_execute(const struct ofpact_multipath *mp, struct flow *flow) +multipath_execute(const struct ofpact_multipath *mp, struct flow *flow, + struct flow_wildcards *wc) { /* Calculate value to store. */ uint32_t hash = flow_hash_fields(flow, mp->fields, mp->basis); uint16_t link = multipath_algorithm(hash, mp->algorithm, mp->max_link + 1, mp->arg); + flow_mask_hash_fields(wc, mp->fields); nxm_reg_load(&mp->dst, link, flow); } diff --git a/lib/multipath.h b/lib/multipath.h index 1b5160dd2..97b51617f 100644 --- a/lib/multipath.h +++ b/lib/multipath.h @@ -22,6 +22,7 @@ struct ds; struct flow; +struct flow_wildcards; struct nx_action_multipath; struct ofpact_multipath; struct ofpbuf; @@ -38,7 +39,8 @@ enum ofperr multipath_check(const struct ofpact_multipath *, void multipath_to_nxast(const struct ofpact_multipath *, struct ofpbuf *openflow); -void multipath_execute(const struct ofpact_multipath *, struct flow *); +void multipath_execute(const struct ofpact_multipath *, struct flow *, + struct flow_wildcards *); void multipath_parse(struct ofpact_multipath *, const char *); void multipath_format(const struct ofpact_multipath *, struct ds *); diff --git a/lib/nx-match.c b/lib/nx-match.c index e111000fc..ecdaa65a2 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -1279,11 +1279,15 @@ nxm_reg_load_to_nxast(const struct ofpact_reg_load *load, void nxm_execute_reg_move(const struct ofpact_reg_move *move, - struct flow *flow) + struct flow *flow, struct flow_wildcards *wc) { + union mf_subvalue mask_value; union mf_value src_value; union mf_value dst_value; + memset(&mask_value, 0xff, sizeof mask_value); + mf_write_subfield_flow(&move->src, &mask_value, &wc->masks); + mf_get_value(move->dst.field, flow, &dst_value); mf_get_value(move->src.field, flow, &src_value); bitwise_copy(&src_value, move->src.field->n_bytes, move->src.ofs, @@ -1428,10 +1432,15 @@ nx_stack_pop(struct ofpbuf *stack) void nxm_execute_stack_push(const struct ofpact_stack *push, - const struct flow *flow, struct ofpbuf *stack) + const struct flow *flow, struct flow_wildcards *wc, + struct ofpbuf *stack) { + union mf_subvalue mask_value; union mf_subvalue dst_value; + memset(&mask_value, 0xff, sizeof mask_value); + mf_write_subfield_flow(&push->subfield, &mask_value, &wc->masks); + mf_read_subfield(&push->subfield, flow, &dst_value); nx_stack_push(stack, &dst_value); } diff --git a/lib/nx-match.h b/lib/nx-match.h index 7d316d800..bb63b90e0 100644 --- a/lib/nx-match.h +++ b/lib/nx-match.h @@ -80,7 +80,8 @@ void nxm_reg_move_to_nxast(const struct ofpact_reg_move *, void nxm_reg_load_to_nxast(const struct ofpact_reg_load *, struct ofpbuf *openflow); -void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *); +void nxm_execute_reg_move(const struct ofpact_reg_move *, struct flow *, + struct flow_wildcards *); void nxm_execute_reg_load(const struct ofpact_reg_load *, struct flow *); void nxm_reg_load(const struct mf_subfield *, uint64_t src_data, struct flow *); @@ -105,7 +106,8 @@ void nxm_stack_pop_to_nxast(const struct ofpact_stack *, struct ofpbuf *openflow); void nxm_execute_stack_push(const struct ofpact_stack *, - const struct flow *, struct ofpbuf *); + const struct flow *, struct flow_wildcards *, + struct ofpbuf *); void nxm_execute_stack_pop(const struct ofpact_stack *, struct flow *, struct ofpbuf *); diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 380b7354f..8df26795f 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -113,9 +113,11 @@ static struct rule_dpif *rule_dpif_cast(const struct rule *rule) } static struct rule_dpif *rule_dpif_lookup(struct ofproto_dpif *, - const struct flow *); + const struct flow *, + struct flow_wildcards *wc); static struct rule_dpif *rule_dpif_lookup__(struct ofproto_dpif *, const struct flow *, + struct flow_wildcards *wc, uint8_t table); static struct rule_dpif *rule_dpif_miss_rule(struct ofproto_dpif *ofproto, const struct flow *flow); @@ -227,6 +229,15 @@ struct initial_vals { }; struct xlate_out { + /* Wildcards relevant in translation. Any fields that were used to + * calculate the action must be set for caching and kernel + * wildcarding to work. For example, if the flow lookup involved + * performing the "normal" action on IPv4 and ARP packets, 'wc' + * would have the 'in_port' (always set), 'dl_type' (flow match), + * 'vlan_tci' (normal action), and 'dl_dst' (normal action) fields + * set. */ + struct flow_wildcards wc; + tag_type tags; /* Tags associated with actions. */ enum slow_path_reason slow; /* 0 if fast path may be used. */ bool has_learn; /* Actions include NXAST_LEARN? */ @@ -360,6 +371,8 @@ static void compose_slow_path(const struct ofproto_dpif *, const struct flow *, static void xlate_report(struct xlate_ctx *ctx, const char *s); +static void xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src); + /* A subfacet (see "struct subfacet" below) has three possible installation * states: * @@ -422,20 +435,26 @@ static int subfacet_install(struct subfacet *, struct dpif_flow_stats *); static void subfacet_uninstall(struct subfacet *); -/* An exact-match instantiation of an OpenFlow flow. +/* A unique, non-overlapping instantiation of an OpenFlow flow. * * A facet associates a "struct flow", which represents the Open vSwitch - * userspace idea of an exact-match flow, with one or more subfacets. Each - * subfacet tracks the datapath's idea of the exact-match flow equivalent to - * the facet. When the kernel module (or other dpif implementation) and Open - * vSwitch userspace agree on the definition of a flow key, there is exactly - * one subfacet per facet. If the dpif implementation supports more-specific - * flow matching than userspace, however, a facet can have more than one - * subfacet, each of which corresponds to some distinction in flow that - * userspace simply doesn't understand. + * userspace idea of an exact-match flow, with one or more subfacets. + * While the facet is created based on an exact-match flow, it is stored + * within the ofproto based on the wildcards that could be expressed + * based on the flow table and other configuration. (See the 'wc' + * description in "struct xlate_out" for more details.) * - * Flow expiration works in terms of subfacets, so a facet must have at least - * one subfacet or it will never expire, leaking memory. */ + * Each subfacet tracks the datapath's idea of the flow equivalent to + * the facet. When the kernel module (or other dpif implementation) and + * Open vSwitch userspace agree on the definition of a flow key, there + * is exactly one subfacet per facet. If the dpif implementation + * supports more-specific flow matching than userspace, however, a facet + * can have more than one subfacet. Examples include the dpif + * implementation not supporting the same wildcards as userspace or some + * distinction in flow that userspace simply doesn't understand. + * + * Flow expiration works in terms of subfacets, so a facet must have at + * least one subfacet or it will never expire, leaking memory. */ struct facet { /* Owners. */ struct hmap_node hmap_node; /* In owning ofproto's 'facets' hmap. */ @@ -447,7 +466,8 @@ struct facet { long long int used; /* Time last used; time created if not used. */ /* Key. */ - struct flow flow; + struct flow flow; /* Flow of the creating subfacet. */ + struct cls_rule cr; /* In 'ofproto_dpif's facets classifier. */ /* These statistics: * @@ -489,14 +509,15 @@ struct facet { long long int learn_rl; /* Rate limiter for facet_learn(). */ }; -static struct facet *facet_create(const struct flow_miss *, uint32_t hash); +static struct facet *facet_create(const struct flow_miss *, struct rule_dpif *, + struct xlate_out *, + struct dpif_flow_stats *); static void facet_remove(struct facet *); static void facet_free(struct facet *); -static struct facet *facet_find(struct ofproto_dpif *, - const struct flow *, uint32_t hash); +static struct facet *facet_find(struct ofproto_dpif *, const struct flow *); static struct facet *facet_lookup_valid(struct ofproto_dpif *, - const struct flow *, uint32_t hash); + const struct flow *); static bool facet_revalidate(struct facet *); static bool facet_check_consistency(struct facet *); @@ -718,7 +739,7 @@ struct ofproto_dpif { bool has_bonded_bundles; /* Facets. */ - struct hmap facets; + struct classifier facets; /* Contains 'struct facet's. */ long long int consistency_rl; /* Revalidation. */ @@ -775,8 +796,8 @@ static void ofproto_trace(struct ofproto_dpif *, const struct flow *, const struct initial_vals *, struct ds *); /* Packet processing. */ -static void update_learning_table(struct ofproto_dpif *, - const struct flow *, int vlan, +static void update_learning_table(struct ofproto_dpif *, const struct flow *, + struct flow_wildcards *, int vlan, struct ofbundle *); /* Upcalls. */ #define FLOW_MISS_MAX_BATCH 50 @@ -1004,12 +1025,14 @@ type_run(const char *type) HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { struct facet *facet, *next; + struct cls_cursor cursor; if (ofproto->backer != backer) { continue; } - HMAP_FOR_EACH_SAFE (facet, next, hmap_node, &ofproto->facets) { + cls_cursor_init(&cursor, &ofproto->facets, NULL); + CLS_CURSOR_FOR_EACH_SAFE (facet, next, cr, &cursor) { if (need_revalidate || tag_set_intersects(&revalidate_set, facet->xout.tags)) { facet_revalidate(facet); @@ -1401,7 +1424,7 @@ construct(struct ofproto *ofproto_) } ofproto->has_bonded_bundles = false; - hmap_init(&ofproto->facets); + classifier_init(&ofproto->facets); ofproto->consistency_rl = LLONG_MIN; for (i = 0; i < N_TABLES; i++) { @@ -1487,7 +1510,7 @@ add_internal_flow(struct ofproto_dpif *ofproto, int id, return error; } - *rulep = rule_dpif_lookup__(ofproto, &fm.match.flow, TBL_INTERNAL); + *rulep = rule_dpif_lookup__(ofproto, &fm.match.flow, NULL, TBL_INTERNAL); ovs_assert(*rulep != NULL); return 0; @@ -1569,7 +1592,7 @@ destruct(struct ofproto *ofproto_) hmap_destroy(&ofproto->bundles); mac_learning_destroy(ofproto->ml); - hmap_destroy(&ofproto->facets); + classifier_destroy(&ofproto->facets); hmap_destroy(&ofproto->vlandev_map); hmap_destroy(&ofproto->realdev_vid_map); @@ -1644,14 +1667,20 @@ run(struct ofproto *ofproto_) /* Check the consistency of a random facet, to aid debugging. */ if (time_msec() >= ofproto->consistency_rl - && !hmap_is_empty(&ofproto->facets) + && !classifier_is_empty(&ofproto->facets) && !ofproto->backer->need_revalidate) { + struct cls_table *table; + struct cls_rule *cr; struct facet *facet; ofproto->consistency_rl = time_msec() + 250; - facet = CONTAINER_OF(hmap_random_node(&ofproto->facets), - struct facet, hmap_node); + table = CONTAINER_OF(hmap_random_node(&ofproto->facets.tables), + struct cls_table, hmap_node); + cr = CONTAINER_OF(hmap_random_node(&table->rules), struct cls_rule, + hmap_node); + facet = CONTAINER_OF(cr, struct facet, cr); + if (!tag_set_intersects(&ofproto->backer->revalidate_set, facet->xout.tags)) { if (!facet_check_consistency(facet)) { @@ -1708,11 +1737,14 @@ static void get_memory_usage(const struct ofproto *ofproto_, struct simap *usage) { const struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_); + struct cls_cursor cursor; size_t n_subfacets = 0; struct facet *facet; - simap_increase(usage, "facets", hmap_count(&ofproto->facets)); - HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) { + simap_increase(usage, "facets", classifier_count(&ofproto->facets)); + + cls_cursor_init(&cursor, &ofproto->facets, NULL); + CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { n_subfacets += list_size(&facet->subfacets); } simap_increase(usage, "subfacets", n_subfacets); @@ -3676,15 +3708,17 @@ handle_flow_miss_common(struct rule_dpif *rule, } /* Figures out whether a flow that missed in 'ofproto', whose details are in - * 'miss', is likely to be worth tracking in detail in userspace and (usually) - * installing a datapath flow. The answer is usually "yes" (a return value of - * true). However, for short flows the cost of bookkeeping is much higher than - * the benefits, so when the datapath holds a large number of flows we impose - * some heuristics to decide which flows are likely to be worth tracking. */ + * 'miss' masked by 'wc', is likely to be worth tracking in detail in userspace + * and (usually) installing a datapath flow. The answer is usually "yes" (a + * return value of true). However, for short flows the cost of bookkeeping is + * much higher than the benefits, so when the datapath holds a large number of + * flows we impose some heuristics to decide which flows are likely to be worth + * tracking. */ static bool -flow_miss_should_make_facet(struct flow_miss *miss, uint32_t hash) +flow_miss_should_make_facet(struct flow_miss *miss, struct flow_wildcards *wc) { struct dpif_backer *backer = miss->ofproto->backer; + uint32_t hash; if (!backer->governor) { size_t n_subfacets; @@ -3697,49 +3731,46 @@ flow_miss_should_make_facet(struct flow_miss *miss, uint32_t hash) backer->governor = governor_create(); } + hash = flow_hash_in_wildcards(&miss->flow, wc, 0); return governor_should_install_flow(backer->governor, hash, list_size(&miss->packets)); } -/* Handles 'miss', which matches 'rule', without creating a facet or subfacet - * or creating any datapath flow. May add an "execute" operation to 'ops' and - * increment '*n_ops'. */ +/* Handles 'miss' without creating a facet or subfacet or creating any datapath + * flow. 'miss->flow' must have matched 'rule' and been xlated into 'xout'. + * May add an "execute" operation to 'ops' and increment '*n_ops'. */ static void -handle_flow_miss_without_facet(struct flow_miss *miss, +handle_flow_miss_without_facet(struct rule_dpif *rule, struct xlate_out *xout, + struct flow_miss *miss, struct flow_miss_op *ops, size_t *n_ops) { - struct rule_dpif *rule = rule_dpif_lookup(miss->ofproto, &miss->flow); - long long int now = time_msec(); struct ofpbuf *packet; - struct xlate_in xin; LIST_FOR_EACH (packet, list_node, &miss->packets) { - struct flow_miss_op *op = &ops[*n_ops]; - struct dpif_flow_stats stats; COVERAGE_INC(facet_suppress); handle_flow_miss_common(rule, packet, &miss->flow); - dpif_flow_stats_extract(&miss->flow, packet, now, &stats); - rule_credit_stats(rule, &stats); + if (xout->slow) { + struct xlate_in xin; - xlate_in_init(&xin, miss->ofproto, &miss->flow, &miss->initial_vals, - rule, stats.tcp_flags, packet); - xin.resubmit_stats = &stats; - xlate_actions(&xin, &op->xout); + xlate_in_init(&xin, miss->ofproto, &miss->flow, + &miss->initial_vals, rule, 0, packet); + xlate_actions_for_side_effects(&xin); + } - if (op->xout.odp_actions.size) { + if (xout->odp_actions.size) { + struct flow_miss_op *op = &ops[*n_ops]; struct dpif_execute *execute = &op->dpif_op.u.execute; init_flow_miss_execute_op(miss, packet, op); + xlate_out_copy(&op->xout, xout); execute->actions = op->xout.odp_actions.data; execute->actions_len = op->xout.odp_actions.size; op->xout_garbage = true; (*n_ops)++; - } else { - xlate_out_uninit(&op->xout); } } } @@ -3752,10 +3783,12 @@ handle_flow_miss_without_facet(struct flow_miss *miss, * here, then the new subfacet or its packets could look (occasionally) as * though it was used some time after the facet was used. That can make a * one-packet flow look like it has a nonzero duration, which looks odd in - * e.g. NetFlow statistics. */ + * e.g. NetFlow statistics. + * + * If non-null, 'stats' will be folded into 'facet'. */ static void handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet, - long long int now, + long long int now, struct dpif_flow_stats *stats, struct flow_miss_op *ops, size_t *n_ops) { struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto); @@ -3764,11 +3797,13 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet, struct ofpbuf *packet; subfacet = subfacet_create(facet, miss, now); - want_path = subfacet->facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH; + want_path = facet->xout.slow ? SF_SLOW_PATH : SF_FAST_PATH; + if (stats) { + subfacet_update_stats(subfacet, stats); + } LIST_FOR_EACH (packet, list_node, &miss->packets) { struct flow_miss_op *op = &ops[*n_ops]; - struct dpif_flow_stats stats; handle_flow_miss_common(facet->rule, packet, &miss->flow); @@ -3780,9 +3815,6 @@ handle_flow_miss_with_facet(struct flow_miss *miss, struct facet *facet, xlate_actions_for_side_effects(&xin); } - dpif_flow_stats_extract(&facet->flow, packet, now, &stats); - subfacet_update_stats(subfacet, &stats); - if (facet->xout.odp_actions.size) { struct dpif_execute *execute = &op->dpif_op.u.execute; @@ -3823,33 +3855,54 @@ handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops, size_t *n_ops) { struct ofproto_dpif *ofproto = miss->ofproto; + struct dpif_flow_stats stats__; + struct dpif_flow_stats *stats = &stats__; + struct ofpbuf *packet; struct facet *facet; long long int now; - uint32_t hash; - /* The caller must ensure that miss->hmap_node.hash contains - * flow_hash(miss->flow, 0). */ - hash = miss->hmap_node.hash; + now = time_msec(); + memset(stats, 0, sizeof *stats); + stats->used = now; + LIST_FOR_EACH (packet, list_node, &miss->packets) { + stats->tcp_flags |= packet_get_tcp_flags(packet, &miss->flow); + stats->n_bytes += packet->size; + stats->n_packets++; + } - facet = facet_lookup_valid(ofproto, &miss->flow, hash); + facet = facet_lookup_valid(ofproto, &miss->flow); if (!facet) { + struct flow_wildcards wc; + struct rule_dpif *rule; + struct xlate_out xout; + struct xlate_in xin; + + flow_wildcards_init_catchall(&wc); + rule = rule_dpif_lookup(ofproto, &miss->flow, &wc); + rule_credit_stats(rule, stats); + + xlate_in_init(&xin, ofproto, &miss->flow, &miss->initial_vals, rule, + stats->tcp_flags, NULL); + xin.resubmit_stats = stats; + xin.may_learn = true; + xlate_actions(&xin, &xout); + flow_wildcards_or(&xout.wc, &xout.wc, &wc); + /* There does not exist a bijection between 'struct flow' and datapath * flow keys with fitness ODP_FIT_TO_LITTLE. This breaks a fundamental * assumption used throughout the facet and subfacet handling code. * Since we have to handle these misses in userspace anyway, we simply * skip facet creation, avoiding the problem altogether. */ if (miss->key_fitness == ODP_FIT_TOO_LITTLE - || !flow_miss_should_make_facet(miss, hash)) { - handle_flow_miss_without_facet(miss, ops, n_ops); + || !flow_miss_should_make_facet(miss, &xout.wc)) { + handle_flow_miss_without_facet(rule, &xout, miss, ops, n_ops); return; } - facet = facet_create(miss, hash); - now = facet->used; - } else { - now = time_msec(); + facet = facet_create(miss, rule, &xout, stats); + stats = NULL; } - handle_flow_miss_with_facet(miss, facet, now, ops, n_ops); + handle_flow_miss_with_facet(miss, facet, now, stats, ops, n_ops); } static struct drop_key * @@ -4651,34 +4704,41 @@ rule_expire(struct rule_dpif *rule) * The caller must already have determined that no facet with an identical * 'miss->flow' exists in 'miss->ofproto'. * - * 'hash' must be the return value of flow_hash(miss->flow, 0). + * 'rule' and 'xout' must have been created based on 'miss'. + * + * 'facet'' statistics are initialized based on 'stats'. * * The facet will initially have no subfacets. The caller should create (at * least) one subfacet with subfacet_create(). */ static struct facet * -facet_create(const struct flow_miss *miss, uint32_t hash) +facet_create(const struct flow_miss *miss, struct rule_dpif *rule, + struct xlate_out *xout, struct dpif_flow_stats *stats) { struct ofproto_dpif *ofproto = miss->ofproto; - struct xlate_in xin; struct facet *facet; + struct match match; facet = xzalloc(sizeof *facet); - facet->used = time_msec(); + facet->packet_count = facet->prev_packet_count = stats->n_packets; + facet->byte_count = facet->prev_byte_count = stats->n_bytes; + facet->tcp_flags = stats->tcp_flags; + facet->used = stats->used; facet->flow = miss->flow; facet->initial_vals = miss->initial_vals; - facet->rule = rule_dpif_lookup(ofproto, &facet->flow); facet->learn_rl = time_msec() + 500; + facet->rule = rule; - hmap_insert(&ofproto->facets, &facet->hmap_node, hash); list_push_back(&facet->rule->facets, &facet->list_node); list_init(&facet->subfacets); netflow_flow_init(&facet->nf_flow); netflow_flow_update_time(ofproto->netflow, &facet->nf_flow, facet->used); - xlate_in_init(&xin, ofproto, &facet->flow, &facet->initial_vals, - facet->rule, 0, NULL); - xin.may_learn = true; - xlate_actions(&xin, &facet->xout); + xlate_out_copy(&facet->xout, xout); + + match_init(&match, &facet->flow, &facet->xout.wc); + cls_rule_init(&facet->cr, &match, OFP_DEFAULT_PRIORITY); + classifier_insert(&ofproto->facets, &facet->cr); + facet->nf_flow.output_iface = facet->xout.nf_output_iface; return facet; @@ -4713,7 +4773,7 @@ execute_odp_actions(struct ofproto_dpif *ofproto, const struct flow *flow, return !error; } -/* Remove 'facet' from 'ofproto' and free up the associated memory: +/* Remove 'facet' from its ofproto and free up the associated memory: * * - If 'facet' was installed in the datapath, uninstalls it and updates its * rule's statistics, via subfacet_uninstall(). @@ -4745,7 +4805,8 @@ facet_remove(struct facet *facet) &facet->subfacets) { subfacet_destroy__(subfacet); } - hmap_remove(&ofproto->facets, &facet->hmap_node); + classifier_remove(&ofproto->facets, &facet->cr); + cls_rule_destroy(&facet->cr); list_remove(&facet->list_node); facet_free(facet); } @@ -4880,41 +4941,28 @@ facet_flush_stats(struct facet *facet) facet->tcp_flags = 0; } -/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'. - * Returns it if found, otherwise a null pointer. - * - * 'hash' must be the return value of flow_hash(flow, 0). +/* Searches 'ofproto''s table of facets for one which would be responsible for + * 'flow'. Returns it if found, otherwise a null pointer. * * The returned facet might need revalidation; use facet_lookup_valid() * instead if that is important. */ static struct facet * -facet_find(struct ofproto_dpif *ofproto, - const struct flow *flow, uint32_t hash) +facet_find(struct ofproto_dpif *ofproto, const struct flow *flow) { - struct facet *facet; - - HMAP_FOR_EACH_WITH_HASH (facet, hmap_node, hash, &ofproto->facets) { - if (flow_equal(flow, &facet->flow)) { - return facet; - } - } - - return NULL; + struct cls_rule *cr = classifier_lookup(&ofproto->facets, flow, NULL); + return cr ? CONTAINER_OF(cr, struct facet, cr) : NULL; } -/* Searches 'ofproto''s table of facets for one exactly equal to 'flow'. - * Returns it if found, otherwise a null pointer. - * - * 'hash' must be the return value of flow_hash(flow, 0). +/* Searches 'ofproto''s table of facets for one capable that covers + * 'flow'. Returns it if found, otherwise a null pointer. * * The returned facet is guaranteed to be valid. */ static struct facet * -facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow, - uint32_t hash) +facet_lookup_valid(struct ofproto_dpif *ofproto, const struct flow *flow) { struct facet *facet; - facet = facet_find(ofproto, flow, hash); + facet = facet_find(ofproto, flow); if (facet && (ofproto->backer->need_revalidate || tag_set_intersects(&ofproto->backer->revalidate_set, @@ -4940,7 +4988,7 @@ facet_check_consistency(struct facet *facet) bool ok; /* Check the rule for consistency. */ - rule = rule_dpif_lookup(ofproto, &facet->flow); + rule = rule_dpif_lookup(ofproto, &facet->flow, NULL); if (rule != facet->rule) { if (!VLOG_DROP_WARN(&rl)) { struct ds s = DS_EMPTY_INITIALIZER; @@ -5013,6 +5061,7 @@ facet_revalidate(struct facet *facet) struct ofproto_dpif *ofproto = ofproto_dpif_cast(facet->rule->up.ofproto); struct rule_dpif *new_rule; struct subfacet *subfacet; + struct flow_wildcards wc; struct xlate_out xout; struct xlate_in xin; @@ -5031,13 +5080,14 @@ facet_revalidate(struct facet *facet) &recv_ofproto, NULL, NULL); if (error || recv_ofproto != ofproto - || memcmp(&recv_flow, &facet->flow, sizeof recv_flow)) { + || facet != facet_find(ofproto, &recv_flow)) { facet_remove(facet); return false; } } - new_rule = rule_dpif_lookup(ofproto, &facet->flow); + flow_wildcards_init_catchall(&wc); + new_rule = rule_dpif_lookup(ofproto, &facet->flow, &wc); /* Calculate new datapath actions. * @@ -5047,11 +5097,18 @@ facet_revalidate(struct facet *facet) xlate_in_init(&xin, ofproto, &facet->flow, &facet->initial_vals, new_rule, 0, NULL); xlate_actions(&xin, &xout); + flow_wildcards_or(&xout.wc, &xout.wc, &wc); /* A facet's slow path reason should only change under dramatic * circumstances. Rather than try to update everything, it's simpler to - * remove the facet and start over. */ - if (facet->xout.slow != xout.slow) { + * remove the facet and start over. + * + * More importantly, if a facet's wildcards change, it will be relatively + * difficult to figure out if its subfacets still belong to it, and if not + * which facet they may belong to. Again, to avoid the complexity, we + * simply give up instead. */ + if (facet->xout.slow != xout.slow + || memcmp(&facet->xout.wc, &xout.wc, sizeof xout.wc)) { facet_remove(facet); xlate_out_uninit(&xout); return false; @@ -5163,9 +5220,11 @@ push_all_stats__(bool run_fast) } HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) { + struct cls_cursor cursor; struct facet *facet; - HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) { + cls_cursor_init(&cursor, &ofproto->facets, NULL); + CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { facet_push_stats(facet, false); if (run_fast) { run_fast_rl(); @@ -5431,12 +5490,15 @@ subfacet_update_stats(struct subfacet *subfacet, /* Rules. */ +/* Lookup 'flow' in 'ofproto''s classifier. If 'wc' is non-null, sets + * the fields that were relevant as part of the lookup. */ static struct rule_dpif * -rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow) +rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow, + struct flow_wildcards *wc) { struct rule_dpif *rule; - rule = rule_dpif_lookup__(ofproto, flow, 0); + rule = rule_dpif_lookup__(ofproto, flow, wc, 0); if (rule) { return rule; } @@ -5446,7 +5508,7 @@ rule_dpif_lookup(struct ofproto_dpif *ofproto, const struct flow *flow) static struct rule_dpif * rule_dpif_lookup__(struct ofproto_dpif *ofproto, const struct flow *flow, - uint8_t table_id) + struct flow_wildcards *wc, uint8_t table_id) { struct cls_rule *cls_rule; struct classifier *cls; @@ -5463,11 +5525,14 @@ rule_dpif_lookup__(struct ofproto_dpif *ofproto, const struct flow *flow, struct flow ofpc_normal_flow = *flow; ofpc_normal_flow.tp_src = htons(0); ofpc_normal_flow.tp_dst = htons(0); - cls_rule = classifier_lookup(cls, &ofpc_normal_flow, NULL); + cls_rule = classifier_lookup(cls, &ofpc_normal_flow, wc); } else if (frag && ofproto->up.frag_handling == OFPC_FRAG_DROP) { cls_rule = &ofproto->drop_frags_rule->up.cr; + if (wc) { + flow_wildcards_init_exact(wc); + } } else { - cls_rule = classifier_lookup(cls, flow, NULL); + cls_rule = classifier_lookup(cls, flow, wc); } return rule_dpif_cast(rule_from_cls_rule(cls_rule)); } @@ -6113,7 +6178,8 @@ xlate_table_action(struct xlate_ctx *ctx, /* Look up a flow with 'in_port' as the input port. */ ctx->xin->flow.in_port = in_port; - rule = rule_dpif_lookup__(ctx->ofproto, &ctx->xin->flow, table_id); + rule = rule_dpif_lookup__(ctx->ofproto, &ctx->xin->flow, + &ctx->xout->wc, table_id); tag_the_flow(ctx, rule); @@ -6231,6 +6297,13 @@ execute_mpls_push_action(struct xlate_ctx *ctx, ovs_be16 eth_type) { ovs_assert(eth_type_mpls(eth_type)); + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); + memset(&ctx->xout->wc.masks.mpls_lse, 0xff, + sizeof ctx->xout->wc.masks.mpls_lse); + memset(&ctx->xout->wc.masks.mpls_depth, 0xff, + sizeof ctx->xout->wc.masks.mpls_depth); + if (ctx->base_flow.mpls_depth) { ctx->xin->flow.mpls_lse &= ~htonl(MPLS_BOS_MASK); ctx->xin->flow.mpls_depth++; @@ -6257,6 +6330,13 @@ execute_mpls_pop_action(struct xlate_ctx *ctx, ovs_be16 eth_type) ovs_assert(eth_type_mpls(ctx->xin->flow.dl_type)); ovs_assert(!eth_type_mpls(eth_type)); + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); + memset(&ctx->xout->wc.masks.mpls_lse, 0xff, + sizeof ctx->xout->wc.masks.mpls_lse); + memset(&ctx->xout->wc.masks.mpls_depth, 0xff, + sizeof ctx->xout->wc.masks.mpls_depth); + if (ctx->xin->flow.mpls_depth) { ctx->xin->flow.mpls_depth--; ctx->xin->flow.mpls_lse = htonl(0); @@ -6377,6 +6457,10 @@ xlate_output_reg_action(struct xlate_ctx *ctx, { uint64_t port = mf_get_subfield(&or->src, &ctx->xin->flow); if (port <= UINT16_MAX) { + union mf_subvalue value; + + memset(&value, 0xff, sizeof value); + mf_write_subfield_flow(&or->src, &value, &ctx->xout->wc.masks); xlate_output_action(ctx, port, or->max_len, false); } } @@ -6462,8 +6546,8 @@ xlate_bundle_action(struct xlate_ctx *ctx, { uint16_t port; - port = bundle_execute(bundle, &ctx->xin->flow, slave_enabled_cb, - ctx->ofproto); + port = bundle_execute(bundle, &ctx->xin->flow, &ctx->xout->wc, + slave_enabled_cb, ctx->ofproto); if (bundle->dst.field) { nxm_reg_load(&bundle->dst, port, &ctx->xin->flow); } else { @@ -6481,6 +6565,14 @@ xlate_learn_action(struct xlate_ctx *ctx, struct ofpbuf ofpacts; int error; + ctx->xout->has_learn = true; + + learn_mask(learn, &ctx->xout->wc); + + if (!ctx->xin->may_learn) { + return; + } + ofpbuf_use_stack(&ofpacts, ofpacts_stub, sizeof ofpacts_stub); learn_execute(learn, &ctx->xin->flow, &fm, &ofpacts); @@ -6646,12 +6738,16 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_SET_IPV4_SRC: + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); if (ctx->xin->flow.dl_type == htons(ETH_TYPE_IP)) { ctx->xin->flow.nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4; } break; case OFPACT_SET_IPV4_DST: + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); if (ctx->xin->flow.dl_type == htons(ETH_TYPE_IP)) { ctx->xin->flow.nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4; } @@ -6659,6 +6755,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_SET_IPV4_DSCP: /* OpenFlow 1.0 only supports IPv4. */ + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); if (ctx->xin->flow.dl_type == htons(ETH_TYPE_IP)) { ctx->xin->flow.nw_tos &= ~IP_DSCP_MASK; ctx->xin->flow.nw_tos |= ofpact_get_SET_IPV4_DSCP(a)->dscp; @@ -6666,6 +6764,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_SET_L4_SRC_PORT: + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); + memset(&ctx->xout->wc.masks.nw_proto, 0xff, + sizeof ctx->xout->wc.masks.nw_proto); if (is_ip_any(&ctx->xin->flow)) { ctx->xin->flow.tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port); @@ -6673,6 +6775,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_SET_L4_DST_PORT: + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); + memset(&ctx->xout->wc.masks.nw_proto, 0xff, + sizeof ctx->xout->wc.masks.nw_proto); if (is_ip_any(&ctx->xin->flow)) { ctx->xin->flow.tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port); @@ -6693,11 +6799,15 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_POP_QUEUE: + memset(&ctx->xout->wc.masks.skb_priority, 0xff, + sizeof ctx->xout->wc.masks.skb_priority); + ctx->xin->flow.skb_priority = ctx->orig_skb_priority; break; case OFPACT_REG_MOVE: - nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->xin->flow); + nxm_execute_reg_move(ofpact_get_REG_MOVE(a), &ctx->xin->flow, + &ctx->xout->wc); break; case OFPACT_REG_LOAD: @@ -6706,7 +6816,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, case OFPACT_STACK_PUSH: nxm_execute_stack_push(ofpact_get_STACK_PUSH(a), &ctx->xin->flow, - &ctx->stack); + &ctx->xout->wc, &ctx->stack); break; case OFPACT_STACK_POP: @@ -6736,6 +6846,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_DEC_TTL: + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); if (compose_dec_ttl(ctx, ofpact_get_DEC_TTL(a))) { goto out; } @@ -6746,7 +6858,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_MULTIPATH: - multipath_execute(ofpact_get_MULTIPATH(a), &ctx->xin->flow); + multipath_execute(ofpact_get_MULTIPATH(a), &ctx->xin->flow, + &ctx->xout->wc); break; case OFPACT_BUNDLE: @@ -6759,10 +6872,7 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_LEARN: - ctx->xout->has_learn = true; - if (ctx->xin->may_learn) { - xlate_learn_action(ctx, ofpact_get_LEARN(a)); - } + xlate_learn_action(ctx, ofpact_get_LEARN(a)); break; case OFPACT_EXIT: @@ -6770,6 +6880,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, break; case OFPACT_FIN_TIMEOUT: + memset(&ctx->xout->wc.masks.dl_type, 0xff, + sizeof ctx->xout->wc.masks.dl_type); + memset(&ctx->xout->wc.masks.nw_proto, 0xff, + sizeof ctx->xout->wc.masks.nw_proto); ctx->xout->has_fin_timeout = true; xlate_fin_timeout(ctx, ofpact_get_FIN_TIMEOUT(a)); break; @@ -6798,7 +6912,8 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len, ctx->table_id = ogt->table_id; /* Look up a flow from the new table. */ - rule = rule_dpif_lookup__(ctx->ofproto, &ctx->xin->flow, ctx->table_id); + rule = rule_dpif_lookup__(ctx->ofproto, &ctx->xin->flow, + &ctx->xout->wc, ctx->table_id); tag_the_flow(ctx, rule); @@ -6917,6 +7032,37 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) memset(&ctx.base_flow.tunnel, 0, sizeof ctx.base_flow.tunnel); ctx.orig_tunnel_ip_dst = ctx.xin->flow.tunnel.ip_dst; + flow_wildcards_init_catchall(&ctx.xout->wc); + memset(&ctx.xout->wc.masks.in_port, 0xff, + sizeof ctx.xout->wc.masks.in_port); + + if (tnl_port_should_receive(&ctx.xin->flow)) { + memset(&ctx.xout->wc.masks.tunnel, 0xff, + sizeof ctx.xout->wc.masks.tunnel); + } + + /* Disable most wildcarding for NetFlow. */ + if (xin->ofproto->netflow) { + memset(&ctx.xout->wc.masks.dl_src, 0xff, + sizeof ctx.xout->wc.masks.dl_src); + memset(&ctx.xout->wc.masks.dl_dst, 0xff, + sizeof ctx.xout->wc.masks.dl_dst); + memset(&ctx.xout->wc.masks.dl_type, 0xff, + sizeof ctx.xout->wc.masks.dl_type); + memset(&ctx.xout->wc.masks.vlan_tci, 0xff, + sizeof ctx.xout->wc.masks.vlan_tci); + memset(&ctx.xout->wc.masks.nw_proto, 0xff, + sizeof ctx.xout->wc.masks.nw_proto); + memset(&ctx.xout->wc.masks.nw_src, 0xff, + sizeof ctx.xout->wc.masks.nw_src); + memset(&ctx.xout->wc.masks.nw_dst, 0xff, + sizeof ctx.xout->wc.masks.nw_dst); + memset(&ctx.xout->wc.masks.tp_src, 0xff, + sizeof ctx.xout->wc.masks.tp_src); + memset(&ctx.xout->wc.masks.tp_dst, 0xff, + sizeof ctx.xout->wc.masks.tp_dst); + } + ctx.xout->tags = 0; ctx.xout->slow = 0; ctx.xout->has_learn = false; @@ -7033,6 +7179,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) } ofpbuf_uninit(&ctx.stack); + + /* Clear the metadata and register wildcard masks, because we won't + * use non-header fields as part of the cache. */ + memset(&ctx.xout->wc.masks.metadata, 0, + sizeof ctx.xout->wc.masks.metadata); + memset(&ctx.xout->wc.masks.regs, 0, sizeof ctx.xout->wc.masks.regs); } /* Translates the 'ofpacts_len' bytes of "struct ofpact"s starting at 'ofpacts' @@ -7053,6 +7205,24 @@ xlate_report(struct xlate_ctx *ctx, const char *s) ctx->xin->report_hook(ctx, s); } } + +static void +xlate_out_copy(struct xlate_out *dst, const struct xlate_out *src) +{ + dst->wc = src->wc; + dst->tags = src->tags; + dst->slow = src->slow; + dst->has_learn = src->has_learn; + dst->has_normal = src->has_normal; + dst->has_fin_timeout = src->has_fin_timeout; + dst->nf_output_iface = src->nf_output_iface; + dst->mirrors = src->mirrors; + + ofpbuf_use_stub(&dst->odp_actions, dst->odp_actions_stub, + sizeof dst->odp_actions_stub); + ofpbuf_put(&dst->odp_actions, src->odp_actions.data, + src->odp_actions.size); +} /* OFPP_NORMAL implementation. */ @@ -7180,7 +7350,7 @@ output_normal(struct xlate_ctx *ctx, const struct ofbundle *out_bundle, port = ofbundle_get_a_port(out_bundle); } else { port = bond_choose_output_slave(out_bundle->bond, &ctx->xin->flow, - vid, &ctx->xout->tags); + &ctx->xout->wc, vid, &ctx->xout->tags); if (!port) { /* No slaves enabled, so drop packet. */ return; @@ -7300,6 +7470,10 @@ add_mirror_actions(struct xlate_ctx *ctx, const struct flow *orig_flow) m = ofproto->mirrors[mirror_mask_ffs(mirrors) - 1]; + if (m->vlans) { + ctx->xout->wc.masks.vlan_tci |= htons(VLAN_CFI | VLAN_VID_MASK); + } + if (!vlan_is_mirrored(m, vlan)) { mirrors = zero_rightmost_1bit(mirrors); continue; @@ -7355,19 +7529,35 @@ update_mirror_stats(struct ofproto_dpif *ofproto, mirror_mask_t mirrors, * migration. Older Citrix-patched Linux DomU used gratuitous ARP replies to * indicate this; newer upstream kernels use gratuitous ARP requests. */ static bool -is_gratuitous_arp(const struct flow *flow) +is_gratuitous_arp(const struct flow *flow, struct flow_wildcards *wc) { - return (flow->dl_type == htons(ETH_TYPE_ARP) - && eth_addr_is_broadcast(flow->dl_dst) - && (flow->nw_proto == ARP_OP_REPLY - || (flow->nw_proto == ARP_OP_REQUEST - && flow->nw_src == flow->nw_dst))); + memset(&wc->masks.dl_type, 0xff, sizeof wc->masks.dl_type); + if (flow->dl_type != htons(ETH_TYPE_ARP)) { + return false; + } + + memset(&wc->masks.dl_dst, 0xff, sizeof wc->masks.dl_dst); + if (!eth_addr_is_broadcast(flow->dl_dst)) { + return false; + } + + memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto); + if (flow->nw_proto == ARP_OP_REPLY) { + return true; + } else if (flow->nw_proto == ARP_OP_REQUEST) { + memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src); + memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst); + + return flow->nw_src == flow->nw_dst; + } else { + return false; + } } static void update_learning_table(struct ofproto_dpif *ofproto, - const struct flow *flow, int vlan, - struct ofbundle *in_bundle) + const struct flow *flow, struct flow_wildcards *wc, + int vlan, struct ofbundle *in_bundle) { struct mac_entry *mac; @@ -7381,7 +7571,7 @@ update_learning_table(struct ofproto_dpif *ofproto, } mac = mac_learning_insert(ofproto->ml, flow->dl_src, vlan); - if (is_gratuitous_arp(flow)) { + if (is_gratuitous_arp(flow, wc)) { /* We don't want to learn from gratuitous ARP packets that are * reflected back over bond slaves so we lock the learning table. */ if (!in_bundle->bond) { @@ -7493,7 +7683,7 @@ is_admissible(struct xlate_ctx *ctx, struct ofport_dpif *in_port, case BV_DROP_IF_MOVED: mac = mac_learning_lookup(ofproto->ml, flow->dl_src, vlan, NULL); if (mac && mac->port.p != in_bundle && - (!is_gratuitous_arp(flow) + (!is_gratuitous_arp(flow, &ctx->xout->wc) || mac_entry_is_grat_arp_locked(mac))) { xlate_report(ctx, "SLB bond thinks this packet looped back, " "dropping"); @@ -7517,6 +7707,13 @@ xlate_normal(struct xlate_ctx *ctx) ctx->xout->has_normal = true; + memset(&ctx->xout->wc.masks.dl_src, 0xff, + sizeof ctx->xout->wc.masks.dl_src); + memset(&ctx->xout->wc.masks.dl_dst, 0xff, + sizeof ctx->xout->wc.masks.dl_dst); + memset(&ctx->xout->wc.masks.vlan_tci, 0xff, + sizeof ctx->xout->wc.masks.vlan_tci); + in_bundle = lookup_input_bundle(ctx->ofproto, ctx->xin->flow.in_port, ctx->xin->packet != NULL, &in_port); if (!in_bundle) { @@ -7564,7 +7761,8 @@ xlate_normal(struct xlate_ctx *ctx) /* Learn source MAC. */ if (ctx->xin->may_learn) { - update_learning_table(ctx->ofproto, &ctx->xin->flow, vlan, in_bundle); + update_learning_table(ctx->ofproto, &ctx->xin->flow, &ctx->xout->wc, + vlan, in_bundle); } /* Determine output bundle. */ @@ -7795,7 +7993,8 @@ send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet) if (subfacet->path == SF_FAST_PATH) { struct dpif_flow_stats stats; - subfacet_install(subfacet, &facet->xout.odp_actions, &stats); + subfacet_install(subfacet, &facet->xout.odp_actions, + &stats); subfacet_update_stats(subfacet, &stats); } } @@ -7811,9 +8010,11 @@ send_active_timeout(struct ofproto_dpif *ofproto, struct facet *facet) static void send_netflow_active_timeouts(struct ofproto_dpif *ofproto) { + struct cls_cursor cursor; struct facet *facet; - HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) { + cls_cursor_init(&cursor, &ofproto->facets, NULL); + CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { send_active_timeout(ofproto, facet); } } @@ -8103,7 +8304,7 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, flow_format(ds, flow); ds_put_char(ds, '\n'); - rule = rule_dpif_lookup(ofproto, flow); + rule = rule_dpif_lookup(ofproto, flow, NULL); trace_format_rule(ds, 0, 0, rule); if (rule == ofproto->miss_rule) { @@ -8119,8 +8320,8 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, if (rule) { uint64_t odp_actions_stub[1024 / 8]; struct ofpbuf odp_actions; - struct trace_ctx trace; + struct match match; uint8_t tcp_flags; tcp_flags = packet ? packet_get_tcp_flags(packet, flow) : 0; @@ -8132,10 +8333,17 @@ ofproto_trace(struct ofproto_dpif *ofproto, const struct flow *flow, packet); trace.xin.resubmit_hook = trace_resubmit; trace.xin.report_hook = trace_report; + xlate_actions(&trace.xin, &trace.xout); ds_put_char(ds, '\n'); trace_format_flow(ds, 0, "Final flow", &trace); + + match_init(&match, flow, &trace.xout.wc); + ds_put_cstr(ds, "Relevant fields: "); + match_format(&match, ds, OFP_DEFAULT_PRIORITY); + ds_put_char(ds, '\n'); + ds_put_cstr(ds, "Datapath actions: "); format_odp_actions(ds, trace.xout.odp_actions.data, trace.xout.odp_actions.size); @@ -8190,11 +8398,13 @@ ofproto_dpif_unclog(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED, static void ofproto_dpif_self_check__(struct ofproto_dpif *ofproto, struct ds *reply) { + struct cls_cursor cursor; struct facet *facet; int errors; errors = 0; - HMAP_FOR_EACH (facet, hmap_node, &ofproto->facets) { + cls_cursor_init(&cursor, &ofproto->facets, NULL); + CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { if (!facet_check_consistency(facet)) { errors++; } @@ -8393,6 +8603,50 @@ ofproto_unixctl_dpif_show(struct unixctl_conn *conn, int argc OVS_UNUSED, ds_destroy(&ds); } +/* Dump the megaflow (facet) cache. This is useful to check the + * correctness of flow wildcarding, since the same mechanism is used for + * both xlate caching and kernel wildcarding. + * + * It's important to note that in the output the flow description uses + * OpenFlow (OFP) ports, but the actions use datapath (ODP) ports. + * + * This command is only needed for advanced debugging, so it's not + * documented in the man page. */ +static void +ofproto_unixctl_dpif_dump_megaflows(struct unixctl_conn *conn, + int argc OVS_UNUSED, const char *argv[], + void *aux OVS_UNUSED) +{ + struct ds ds = DS_EMPTY_INITIALIZER; + const struct ofproto_dpif *ofproto; + long long int now = time_msec(); + struct cls_cursor cursor; + struct facet *facet; + + ofproto = ofproto_dpif_lookup(argv[1]); + if (!ofproto) { + unixctl_command_reply_error(conn, "no such bridge"); + return; + } + + cls_cursor_init(&cursor, &ofproto->facets, NULL); + CLS_CURSOR_FOR_EACH (facet, cr, &cursor) { + cls_rule_format(&facet->cr, &ds); + ds_put_cstr(&ds, ", "); + ds_put_format(&ds, "n_subfacets:%"PRIu64", ", + list_size(&facet->subfacets)); + ds_put_format(&ds, "used:%.3fs, ", (now - facet->used) / 1000.0); + ds_put_cstr(&ds, "Datapath actions: "); + format_odp_actions(&ds, facet->xout.odp_actions.data, + facet->xout.odp_actions.size); + ds_put_cstr(&ds, "\n"); + } + + ds_chomp(&ds, '\n'); + unixctl_command_reply(conn, ds_cstr(&ds)); + ds_destroy(&ds); +} + static void ofproto_unixctl_dpif_dump_flows(struct unixctl_conn *conn, int argc OVS_UNUSED, const char *argv[], @@ -8504,6 +8758,8 @@ ofproto_dpif_unixctl_init(void) ofproto_unixctl_dpif_dump_flows, NULL); unixctl_command_register("dpif/del-flows", "bridge", 1, 1, ofproto_unixctl_dpif_del_flows, NULL); + unixctl_command_register("dpif/dump-megaflows", "bridge", 1, 1, + ofproto_unixctl_dpif_dump_megaflows, NULL); } /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.) diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at index f9cde8e74..e8458d0f5 100644 --- a/tests/ofproto-dpif.at +++ b/tests/ofproto-dpif.at @@ -2209,3 +2209,347 @@ OFPST_PORT reply (OF1.3) (xid=0x2): 3 ports ]) OVS_VSWITCHD_STOP AT_CLEANUP + +dnl ---------------------------------------------------------------------- +AT_BANNER([ofproto-dpif -- megaflows]) + +# Strips out uninteresting parts of megaflow output, as well as parts +# that vary from one run to another (e.g., timing and bond actions). +m4_define([STRIP_XOUT], [[sed ' + s/used:[0-9]*\.[0-9]*/used:0.0/ + s/Datapath actions:.*/Datapath actions: <del>/ +' | sort]]) + +AT_SETUP([ofproto-dpif megaflow - port classification]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1 actions=output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +in_port=1, n_subfacets:2, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - L2 classification]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1,dl_src=50:54:00:00:00:09 actions=output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +in_port=1,dl_src=50:54:00:00:00:09, n_subfacets:1, used:0.0s, Datapath actions: <del> +in_port=1,dl_src=50:54:00:00:00:0b, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - L3 classification]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1,icmp,nw_src=10.0.0.4 actions=output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +icmp,in_port=1,nw_src=10.0.0.2, n_subfacets:1, used:0.0s, Datapath actions: <del> +icmp,in_port=1,nw_src=10.0.0.4, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - L4 classification]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1,icmp,icmp_type=8 actions=output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +icmp,in_port=1,icmp_type=8, n_subfacets:2, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - normal]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - mpls]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 dl_src=50:54:00:00:00:09 actions=push_mpls:0x8847,2 +table=0 dl_src=50:54:00:00:00:0b actions=pop_mpls:0x0800,2 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0a),eth_type(0x8847),mpls(label=11,tc=3,ttl=64,bos=1)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +mpls,in_port=1,dl_src=50:54:00:00:00:09,mpls_label=11,mpls_tc=3,mpls_ttl=64,mpls_bos=1, n_subfacets:1, used:0.0s, Datapath actions: <del> +mpls,in_port=1,dl_src=50:54:00:00:00:0b,mpls_label=11,mpls_tc=3,mpls_ttl=64,mpls_bos=1, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - netflow]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) + +dnl NetFlow configuration disables wildcarding relevant fields +ON_EXIT([kill `cat test-netflow.pid`]) +AT_CHECK([test-netflow --log-file --detach --no-chdir --pidfile 0:127.0.0.1 > netflow.log], [0], [], [ignore]) +AT_CAPTURE_FILE([netflow.log]) +NETFLOW_PORT=`parse_listening_port < test-netflow.log` +ovs-vsctl \ + set Bridge br0 netflow=@nf -- \ + --id=@nf create NetFlow targets=\"127.0.0.1:$NETFLOW_PORT\" \ + engine_id=1 engine_type=2 active_timeout=30 add-id-to-interface=false + +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del> +icmp,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - normal, active-backup bonding]) +OVS_VSWITCHD_START( + [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ + add-bond br0 bond0 p2 p3 bond_mode=active-backup -- \ + set interface p2 type=dummy ofport_request=2 -- \ + set interface p3 type=dummy ofport_request=3]) +AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK +]) + +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - normal, balance-slb bonding]) +OVS_VSWITCHD_START( + [add-port br0 p1 -- set Interface p1 type=dummy ofport_request=1 -- \ + add-bond br0 bond0 p2 p3 bond_mode=balance-slb -- \ + set interface p2 type=dummy ofport_request=2 -- \ + set interface p3 type=dummy ofport_request=3]) +AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK +]) + +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - normal, balance-tcp bonding]) +# Create bond0 on br0 with interfaces p0 and p1 +# and bond1 on br1 with interfaces p2 and p3 +# with p0 patched to p2 and p1 patched to p3. +OVS_VSWITCHD_START( + [add-bond br0 bond0 p0 p1 bond_mode=balance-tcp lacp=active \ + other-config:lacp-time=fast \ + other-config:bond-rebalance-interval=0 -- \ + set interface p0 type=patch options:peer=p2 ofport_request=1 -- \ + set interface p1 type=patch options:peer=p3 ofport_request=2 -- \ + add-br br1 -- \ + set bridge br1 other-config:hwaddr=aa:66:aa:66:00:00 -- \ + set bridge br1 datapath-type=dummy other-config:datapath-id=1234 \ + fail-mode=secure -- \ + add-bond br1 bond1 p2 p3 bond_mode=balance-tcp lacp=active \ + other-config:lacp-time=fast \ + other-config:bond-rebalance-interval=0 -- \ + set interface p2 type=patch options:peer=p0 ofport_request=3 -- \ + set interface p3 type=patch options:peer=p1 ofport_request=4 --]) + +AT_CHECK([ovs-appctl netdev-dummy/set-admin-state up], 0, [OK +]) +ADD_OF_PORTS([br0], [7]) +AT_CHECK([ovs-ofctl add-flow br0 action=normal]) +AT_CHECK([ovs-ofctl add-flow br1 action=normal]) +ovs-appctl time/warp 5000 +AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p7 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) + +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +icmp,in_port=7,vlan_tci=0x0000,dl_src=50:54:00:00:00:09,dl_dst=50:54:00:00:00:0a,nw_src=10.0.0.2,nw_dst=10.0.0.1,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del> +icmp,in_port=7,vlan_tci=0x0000,dl_src=50:54:00:00:00:0b,dl_dst=50:54:00:00:00:0c,nw_src=10.0.0.4,nw_dst=10.0.0.3,icmp_type=8,icmp_code=0, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - resubmit port action]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1,ip actions=resubmit(90) +table=0 in_port=90,dl_src=50:54:00:00:00:09 actions=output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,dl_src=50:54:00:00:00:09, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,dl_src=50:54:00:00:00:0b, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - resubmit table action]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1,ip actions=resubmit(,1) +table=1 dl_src=50:54:00:00:00:09 actions=output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto= +1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,dl_src=50:54:00:00:00:09, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,dl_src=50:54:00:00:00:0b, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - goto_table action]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1,ip actions=goto_table(1) +table=1 dl_src=50:54:00:00:00:09 actions=output(2) +]) +AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,dl_src=50:54:00:00:00:09, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,dl_src=50:54:00:00:00:0b, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - mirroring, select_all]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2], [3]) +ovs-vsctl \ + set Bridge br0 mirrors=@m --\ + --id=@p3 get Port p3 --\ + --id=@m create Mirror name=mymirror select_all=true output_port=@p3 + +AT_DATA([flows.txt], [dnl +in_port=1 actions=output:2 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +in_port=1, n_subfacets:2, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - mirroring, select_vlan]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2], [3]) +ovs-vsctl \ + set Bridge br0 mirrors=@m --\ + --id=@p2 get Port p2 -- --id=@p3 get Port p3 --\ + --id=@m create Mirror name=mymirror select_all=true select_vlan=11 output_port=@p3 + +AT_DATA([flows.txt], [dnl +in_port=1 actions=output:2 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x8100),vlan(vid=11,pcp=7),encap(eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0))']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +in_port=1,dl_vlan=11, n_subfacets:1, used:0.0s, Datapath actions: <del> +in_port=1,vlan_tci=0x0000/0x1fff, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - move action]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1 ip,actions=move:NXM_OF_IP_SRC[[]]->NXM_NX_REG0[[]],resubmit(90) +table=0 in_port=90 ip,actions=move:NXM_NX_REG0[[]]->NXM_NX_REG1[[]],resubmit(91) +table=0 in_port=91 reg0=0x0a000002,actions=output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,nw_src=10.0.0.2, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,nw_src=10.0.0.4, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - push action]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1 ip,actions=push:NXM_OF_IP_SRC[[]],output(2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +ip,in_port=1,nw_src=10.0.0.2, n_subfacets:1, used:0.0s, Datapath actions: <del> +ip,in_port=1,nw_src=10.0.0.4, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([ofproto-dpif megaflow - learning]) +OVS_VSWITCHD_START +ADD_OF_PORTS([br0], [1], [2]) +AT_DATA([flows.txt], [dnl +table=0 in_port=1 actions=load:2->NXM_NX_REG0[[0..15]],learn(table=1,priority=65535,NXM_OF_ETH_SRC[[]],NXM_OF_VLAN_TCI[[0..11]],output:NXM_NX_REG0[[0..15]]),output:2 +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:09,dst=50:54:00:00:00:0a),eth_type(0x0800),ipv4(src=10.0.0.2,dst=10.0.0.1,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +AT_CHECK([ovs-appctl netdev-dummy/receive p1 'in_port(1),eth(src=50:54:00:00:00:0b,dst=50:54:00:00:00:0c),eth_type(0x0800),ipv4(src=10.0.0.4,dst=10.0.0.3,proto=1,tos=0,ttl=64,frag=no),icmp(type=8,code=0)']) +dnl The original flow is missing due to a revalidation. +AT_CHECK([ovs-appctl dpif/dump-megaflows br0 | STRIP_XOUT], [0], [dnl +in_port=1,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:09, n_subfacets:1, used:0.0s, Datapath actions: <del> +in_port=1,vlan_tci=0x0000/0x0fff,dl_src=50:54:00:00:00:0b, n_subfacets:1, used:0.0s, Datapath actions: <del> +]) +OVS_VSWITCHD_STOP +AT_CLEANUP diff --git a/tests/test-bundle.c b/tests/test-bundle.c index f5b24b4a3..c47599125 100644 --- a/tests/test-bundle.c +++ b/tests/test-bundle.c @@ -187,9 +187,11 @@ main(int argc, char *argv[]) for (j = 0; j < N_FLOWS; j++) { struct flow *flow = &flows[j]; uint16_t old_slave_id, ofp_port; + struct flow_wildcards wc; old_slave_id = flow->regs[0]; - ofp_port = bundle_execute(bundle, flow, slave_enabled_cb, &sg); + ofp_port = bundle_execute(bundle, flow, &wc, slave_enabled_cb, + &sg); flow->regs[0] = ofp_port; if (ofp_port != OFPP_NONE) { diff --git a/tests/test-multipath.c b/tests/test-multipath.c index 8442bc2e9..e8aacfff4 100644 --- a/tests/test-multipath.c +++ b/tests/test-multipath.c @@ -57,6 +57,7 @@ main(int argc, char *argv[]) memset(histogram, 0, sizeof histogram); for (i = 0; i < N_FLOWS; i++) { int old_link, new_link; + struct flow_wildcards wc; struct flow flow; random_bytes(&flow, sizeof flow); @@ -64,11 +65,11 @@ main(int argc, char *argv[]) flow.mpls_depth = 0; mp.max_link = n - 1; - multipath_execute(&mp, &flow); + multipath_execute(&mp, &flow, &wc); old_link = flow.regs[0]; mp.max_link = n; - multipath_execute(&mp, &flow); + multipath_execute(&mp, &flow, &wc); new_link = flow.regs[0]; assert(old_link >= 0 && old_link < n); |