From 7b514aba0e91c535024508624724a83a3df87b71 Mon Sep 17 00:00:00 2001 From: Nobuhiro MIKI Date: Wed, 15 Nov 2023 18:47:33 +0900 Subject: [PATCH] ofproto-dpif-trace: Improve conjunctive match tracing. A conjunctive flow consists of two or more multiple flows with conjunction actions. When input to the ofproto/trace command matches a conjunctive flow, it outputs flows of all dimensions. Acked-by: Simon Horman Signed-off-by: Nobuhiro MIKI Signed-off-by: Ilya Maximets --- NEWS | 2 + lib/classifier.c | 51 ++++++++++++++++--- lib/classifier.h | 4 +- lib/ovs-router.c | 5 +- lib/tnl-ports.c | 6 +-- ofproto/ofproto-dpif-xlate.c | 67 +++++++++++++++++++++--- ofproto/ofproto-dpif.c | 25 ++++++--- ofproto/ofproto-dpif.h | 3 +- tests/classifier.at | 99 ++++++++++++++++++++++++++++++++++++ tests/test-classifier.c | 8 +-- 10 files changed, 240 insertions(+), 30 deletions(-) diff --git a/NEWS b/NEWS index 43aea97b5d2..1d9c30533b3 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,8 @@ Post-v3.2.0 during the process, and error logs complaining unrecognized fields may be observed on old nodes. - ovs-appctl: + * 'ofproto/trace' now reports OpenFlow rules that make up a conjunctive + flow match. * Output of 'dpctl/show' command no longer shows interface configuration status, only values of the actual configuration options, a.k.a. 'requested' configuration. The interface configuration status, diff --git a/lib/classifier.c b/lib/classifier.c index 18dbfc83ad4..0729bd19024 100644 --- a/lib/classifier.c +++ b/lib/classifier.c @@ -853,6 +853,32 @@ trie_ctx_init(struct trie_ctx *ctx, const struct cls_trie *trie) ctx->lookup_done = false; } +static void +insert_conj_flows(struct hmapx *conj_flows, uint32_t id, int priority, + struct cls_conjunction_set **soft, size_t n_soft) +{ + struct cls_conjunction_set *conj_set; + + if (!conj_flows) { + return; + } + + for (size_t i = 0; i < n_soft; i++) { + conj_set = soft[i]; + + if (conj_set->priority != priority) { + continue; + } + + for (size_t j = 0; j < conj_set->n; j++) { + if (conj_set->conj[j].id == id) { + hmapx_add(conj_flows, (void *) (conj_set->match->cls_rule)); + break; + } + } + } +} + struct conjunctive_match { struct hmap_node hmap_node; uint32_t id; @@ -933,11 +959,15 @@ free_conjunctive_matches(struct hmap *matches, * recursion within this function itself. * * 'flow' is non-const to allow for temporary modifications during the lookup. - * Any changes are restored before returning. */ + * Any changes are restored before returning. + * + * 'conj_flows' is an optional parameter. If it is non-null, the matching + * conjunctive flows are inserted. */ static const struct cls_rule * classifier_lookup__(const struct classifier *cls, ovs_version_t version, struct flow *flow, struct flow_wildcards *wc, - bool allow_conjunctive_matches) + bool allow_conjunctive_matches, + struct hmapx *conj_flows) { struct trie_ctx trie_ctx[CLS_MAX_TRIES]; const struct cls_match *match; @@ -1097,10 +1127,15 @@ classifier_lookup__(const struct classifier *cls, ovs_version_t version, const struct cls_rule *rule; flow->conj_id = id; - rule = classifier_lookup__(cls, version, flow, wc, false); + rule = classifier_lookup__(cls, version, flow, wc, false, + NULL); flow->conj_id = saved_conj_id; if (rule) { + if (allow_conjunctive_matches) { + insert_conj_flows(conj_flows, id, soft_pri, soft, + n_soft); + } free_conjunctive_matches(&matches, cm_stubs, ARRAY_SIZE(cm_stubs)); if (soft != soft_stub) { @@ -1161,12 +1196,16 @@ classifier_lookup__(const struct classifier *cls, ovs_version_t version, * flow_wildcards_init_catchall()). * * 'flow' is non-const to allow for temporary modifications during the lookup. - * Any changes are restored before returning. */ + * Any changes are restored before returning. + * + * 'conj_flows' is an optional parameter. If it is non-null, the matching + * conjunctive flows are inserted. */ const struct cls_rule * classifier_lookup(const struct classifier *cls, ovs_version_t version, - struct flow *flow, struct flow_wildcards *wc) + struct flow *flow, struct flow_wildcards *wc, + struct hmapx *conj_flows) { - return classifier_lookup__(cls, version, flow, wc, true); + return classifier_lookup__(cls, version, flow, wc, true, conj_flows); } /* Finds and returns a rule in 'cls' with exactly the same priority and diff --git a/lib/classifier.h b/lib/classifier.h index f646a8f7429..f55a2cba998 100644 --- a/lib/classifier.h +++ b/lib/classifier.h @@ -299,6 +299,7 @@ * parallel to the rule's removal. */ #include "cmap.h" +#include "hmapx.h" #include "openvswitch/match.h" #include "openvswitch/meta-flow.h" #include "pvector.h" @@ -398,7 +399,8 @@ static inline void classifier_publish(struct classifier *); * and each other. */ const struct cls_rule *classifier_lookup(const struct classifier *, ovs_version_t, struct flow *, - struct flow_wildcards *); + struct flow_wildcards *, + struct hmapx *conj_flows); bool classifier_rule_overlaps(const struct classifier *, const struct cls_rule *, ovs_version_t); const struct cls_rule *classifier_find_rule_exactly(const struct classifier *, diff --git a/lib/ovs-router.c b/lib/ovs-router.c index 7c04bb0e6b1..ca014d80ed3 100644 --- a/lib/ovs-router.c +++ b/lib/ovs-router.c @@ -115,7 +115,8 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst, const struct cls_rule *cr_src; struct flow flow_src = {.ipv6_dst = *src, .pkt_mark = mark}; - cr_src = classifier_lookup(&cls, OVS_VERSION_MAX, &flow_src, NULL); + cr_src = classifier_lookup(&cls, OVS_VERSION_MAX, &flow_src, NULL, + NULL); if (cr_src) { struct ovs_router_entry *p_src = ovs_router_entry_cast(cr_src); if (!p_src->local) { @@ -126,7 +127,7 @@ ovs_router_lookup(uint32_t mark, const struct in6_addr *ip6_dst, } } - cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL); + cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL); if (cr) { struct ovs_router_entry *p = ovs_router_entry_cast(cr); diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c index f16409a0bf0..bb0b0b0c55f 100644 --- a/lib/tnl-ports.c +++ b/lib/tnl-ports.c @@ -112,7 +112,7 @@ map_insert(odp_port_t port, struct eth_addr mac, struct in6_addr *addr, tnl_port_init_flow(&match.flow, mac, addr, nw_proto, tp_port); do { - cr = classifier_lookup(&cls, OVS_VERSION_MAX, &match.flow, NULL); + cr = classifier_lookup(&cls, OVS_VERSION_MAX, &match.flow, NULL, NULL); p = tnl_port_cast(cr); /* Try again if the rule was released before we get the reference. */ } while (p && !ovs_refcount_try_ref_rcu(&p->ref_cnt)); @@ -247,7 +247,7 @@ map_delete(struct eth_addr mac, struct in6_addr *addr, tnl_port_init_flow(&flow, mac, addr, nw_proto, tp_port); - cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL); + cr = classifier_lookup(&cls, OVS_VERSION_MAX, &flow, NULL, NULL); tnl_port_unref(cr); } @@ -305,7 +305,7 @@ odp_port_t tnl_port_map_lookup(struct flow *flow, struct flow_wildcards *wc) { const struct cls_rule *cr = classifier_lookup(&cls, OVS_VERSION_MAX, flow, - wc); + wc, NULL); return (cr) ? tnl_port_cast(cr)->portno : ODPP_NONE; } diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index e243773307b..289f8a7361d 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -229,6 +229,9 @@ struct xlate_ctx { * wants actions. */ struct ofpbuf *odp_actions; + /* Set of matching conjunctive flows, or NULL. */ + struct hmapx *conj_flows; + /* Statistics maintained by xlate_table_action(). * * These statistics limit the amount of work that a single flow @@ -866,6 +869,34 @@ xlate_report_action_set(const struct xlate_ctx *ctx, const char *verb) } } +static void +xlate_report_conj_matches(const struct xlate_ctx *ctx, + const struct ofputil_port_map *map) +{ + struct ds s = DS_EMPTY_INITIALIZER; + struct hmapx_node *node; + struct cls_rule *rule; + + /* NOTE: The conj flows have meaning in order. For each flow that is a + * component of conj flows, 'k' in 'conjunction(id, k/n)' represents the + * dimension. When there are multiple flows with the same id, it may be + * implicitly expected that they would be output in ascending order of 'k'. + * + * However, because of the use of hmapx strucutre and the fact that the + * classifier returns them in arbitrary order, they are output in arbitrary + * order here. */ + HMAPX_FOR_EACH (node, ctx->conj_flows) { + ds_clear(&s); + + rule = node->data; + + cls_rule_format(rule, ofproto_get_tun_tab(&ctx->xin->ofproto->up), + map, &s); + xlate_report(ctx, OFT_DETAIL, "conj. %s", ds_cstr(&s)); + } + + ds_destroy(&s); +} /* If tracing is enabled in 'ctx', appends a node representing 'rule' (in * OpenFlow table 'table_id') to the trace and makes this node the parent for @@ -882,6 +913,8 @@ xlate_report_table(const struct xlate_ctx *ctx, struct rule_dpif *rule, return; } + struct ofputil_port_map map = OFPUTIL_PORT_MAP_INITIALIZER(&map); + struct ds s = DS_EMPTY_INITIALIZER; ds_put_format(&s, "%2d. ", table_id); if (rule == ctx->xin->ofproto->miss_rule) { @@ -892,8 +925,6 @@ xlate_report_table(const struct xlate_ctx *ctx, struct rule_dpif *rule, ds_put_cstr(&s, "Packets are IP fragments and " "the fragment handling mode is \"drop\"."); } else { - struct ofputil_port_map map = OFPUTIL_PORT_MAP_INITIALIZER(&map); - if (ctx->xin->names) { struct ofproto_dpif *ofprotop; ofprotop = ofproto_dpif_lookup_by_name(ctx->xbridge->name); @@ -904,8 +935,6 @@ xlate_report_table(const struct xlate_ctx *ctx, struct rule_dpif *rule, ofproto_get_tun_tab(&ctx->xin->ofproto->up), &map, &s, OFP_DEFAULT_PRIORITY); - ofputil_port_map_destroy(&map); - if (ds_last(&s) != ' ') { ds_put_cstr(&s, ", "); } @@ -918,6 +947,9 @@ xlate_report_table(const struct xlate_ctx *ctx, struct rule_dpif *rule, ctx->xin->trace = &oftrace_report(ctx->xin->trace, OFT_TABLE, ds_cstr(&s))->subs; ds_destroy(&s); + + xlate_report_conj_matches(ctx, &map); + ofputil_port_map_destroy(&map); } /* If tracing is enabled in 'ctx', adds an OFT_DETAIL trace node to 'ctx' @@ -4653,7 +4685,7 @@ xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id, ctx->xin->resubmit_stats, &ctx->table_id, in_port, may_packet_in, honor_table_miss, - ctx->xin->xcache); + ctx->xin->xcache, ctx->conj_flows); /* Swap back. */ if (with_ct_orig) { tuple_swap(&ctx->xin->flow, ctx->wc); @@ -4674,6 +4706,11 @@ xlate_table_action(struct xlate_ctx *ctx, ofp_port_t in_port, uint8_t table_id, struct ovs_list *old_trace = ctx->xin->trace; xlate_report_table(ctx, rule, table_id); + + if (OVS_UNLIKELY(ctx->xin->trace)) { + hmapx_clear(ctx->conj_flows); + } + xlate_recursively(ctx, rule, table_id <= old_table_id, is_last_action, xlator); ctx->xin->trace = old_trace; @@ -8044,6 +8081,13 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) COVERAGE_INC(xlate_actions); + ctx.conj_flows = NULL; + + if (OVS_UNLIKELY(xin->trace)) { + ctx.conj_flows = xzalloc(sizeof *ctx.conj_flows); + hmapx_init(ctx.conj_flows); + } + xin->trace = xlate_report(&ctx, OFT_BRIDGE, "bridge(\"%s\")", xbridge->name); if (xin->frozen_state) { @@ -8181,7 +8225,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) ctx.rule = rule_dpif_lookup_from_table( ctx.xbridge->ofproto, ctx.xin->tables_version, flow, ctx.wc, ctx.xin->resubmit_stats, &ctx.table_id, - flow->in_port.ofp_port, true, true, ctx.xin->xcache); + flow->in_port.ofp_port, true, true, ctx.xin->xcache, + ctx.conj_flows); if (ctx.xin->resubmit_stats) { rule_dpif_credit_stats(ctx.rule, ctx.xin->resubmit_stats, false); } @@ -8194,6 +8239,10 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) } xlate_report_table(&ctx, ctx.rule, ctx.table_id); + + if (OVS_UNLIKELY(ctx.xin->trace)) { + hmapx_clear(ctx.conj_flows); + } } /* Tunnel stats only for not-thawed packets. */ @@ -8375,6 +8424,12 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) ofpbuf_uninit(&scratch_actions); ofpbuf_delete(ctx.encap_data); + /* Clean up 'conj_flows' as it is no longer needed. */ + if (OVS_UNLIKELY(xin->trace)) { + hmapx_destroy(ctx.conj_flows); + free(ctx.conj_flows); + } + /* Make sure we return a "drop flow" in case of an error. */ if (ctx.error) { xout->slow = 0; diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 9e8faf82910..54e057d43ff 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -4383,15 +4383,20 @@ ofproto_dpif_get_tables_version(struct ofproto_dpif *ofproto) * a reference. * * 'flow' is non-const to allow for temporary modifications during the lookup. - * Any changes are restored before returning. */ + * Any changes are restored before returning. + * + * 'conj_flows' is an optional parameter. If it is non-null, the matching + * conjunctive flows are inserted. */ static struct rule_dpif * rule_dpif_lookup_in_table(struct ofproto_dpif *ofproto, ovs_version_t version, uint8_t table_id, struct flow *flow, - struct flow_wildcards *wc) + struct flow_wildcards *wc, + struct hmapx *conj_flows) { struct classifier *cls = &ofproto->up.tables[table_id].cls; return rule_dpif_cast(rule_from_cls_rule(classifier_lookup(cls, version, - flow, wc))); + flow, wc, + conj_flows))); } void @@ -4433,7 +4438,10 @@ ofproto_dpif_credit_table_stats(struct ofproto_dpif *ofproto, uint8_t table_id, * 'in_port'. This is needed for resubmit action support. * * 'flow' is non-const to allow for temporary modifications during the lookup. - * Any changes are restored before returning. */ + * Any changes are restored before returning. + * + * 'conj_flows' is an optional parameter. If it is non-null, the matching + * conjunctive flows are inserted. */ struct rule_dpif * rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, ovs_version_t version, struct flow *flow, @@ -4441,7 +4449,8 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, const struct dpif_flow_stats *stats, uint8_t *table_id, ofp_port_t in_port, bool may_packet_in, bool honor_table_miss, - struct xlate_cache *xcache) + struct xlate_cache *xcache, + struct hmapx *conj_flows) { ovs_be16 old_tp_src = flow->tp_src, old_tp_dst = flow->tp_dst; ofp_port_t old_in_port = flow->in_port.ofp_port; @@ -4497,7 +4506,8 @@ rule_dpif_lookup_from_table(struct ofproto_dpif *ofproto, next_id++, next_id += (next_id == TBL_INTERNAL)) { *table_id = next_id; - rule = rule_dpif_lookup_in_table(ofproto, version, next_id, flow, wc); + rule = rule_dpif_lookup_in_table(ofproto, version, next_id, flow, wc, + conj_flows); if (stats) { struct oftable *tbl = &ofproto->up.tables[next_id]; unsigned long orig; @@ -6680,7 +6690,8 @@ ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto, rule = rule_dpif_lookup_in_table(ofproto, ofproto_dpif_get_tables_version(ofproto), - TBL_INTERNAL, &match->flow, &match->wc); + TBL_INTERNAL, &match->flow, &match->wc, + NULL); if (rule) { *rulep = &rule->up; } else { diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h index d8e0cd37ac5..1fe22ab41bd 100644 --- a/ofproto/ofproto-dpif.h +++ b/ofproto/ofproto-dpif.h @@ -103,7 +103,8 @@ struct rule_dpif *rule_dpif_lookup_from_table(struct ofproto_dpif *, ofp_port_t in_port, bool may_packet_in, bool honor_table_miss, - struct xlate_cache *); + struct xlate_cache *, + struct hmapx *conj_flows); void rule_dpif_credit_stats(struct rule_dpif *, const struct dpif_flow_stats *, bool); diff --git a/tests/classifier.at b/tests/classifier.at index de2705653e0..93a13f32b13 100644 --- a/tests/classifier.at +++ b/tests/classifier.at @@ -276,6 +276,13 @@ for src in 0 1 2 3 4 5 6 7; do AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_type=0x0800,nw_src=10.0.0.$src,nw_dst=10.0.0.$dst"], [0], [stdout]) AT_CHECK_UNQUOTED([tail -1 stdout], [0], [Datapath actions: $out ]) + dnl Check detailed output for conjunctive match. + if test $out = 3; then + AT_CHECK_UNQUOTED([cat stdout | grep conj\\. | sort], [0], [dnl + -> conj. priority=100,ip,nw_dst=10.0.0.$dst + -> conj. priority=100,ip,nw_src=10.0.0.$src +]) + fi done done OVS_VSWITCHD_STOP @@ -418,6 +425,98 @@ ovs-ofctl: "conjunction" actions may be used along with "note" but not any other OVS_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([conjunctive match with same priority]) +OVS_VSWITCHD_START +add_of_ports br0 1 2 +AT_DATA([flows.txt], [dnl +conj_id=1,actions=2 +conj_id=2,actions=drop + +priority=10,ip,ip_dst=10.0.0.1,actions=conjunction(1,1/2) +priority=10,ip,ip_src=10.0.0.2,actions=conjunction(1,2/2) +priority=10,ip,ip_dst=10.0.0.3,actions=conjunction(2,1/2) +priority=10,ip,in_port=1,actions=conjunction(2,2/2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +# Check that "priority=10,ip,in_port=1,actions=conjunction(2,2/2)" is +# correctly excluded from the output. +AT_CHECK([ovs-appctl ofproto/trace br0 "in_port=1,dl_type=0x0800,nw_dst=10.0.0.1,nw_src=10.0.0.2" | grep conj\\. | sort], [0], [dnl + -> conj. priority=10,ip,nw_dst=10.0.0.1 + -> conj. priority=10,ip,nw_src=10.0.0.2 +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([conjunctive match with metadata]) +OVS_VSWITCHD_START +AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=0,len=4}->tun_metadata0"]) +AT_CHECK([ovs-ofctl add-tlv-map br0 "{class=0xffff,type=1,len=8}->tun_metadata1"]) +AT_DATA([flows.txt], [dnl +conj_id=7,actions=drop + +priority=5,tun_metadata0=0x1,actions=conjunction(7,1/2) +priority=5,tun_metadata1=0x2,actions=conjunction(7,2/2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +# Check that tunnel metadata is included in the output. +AT_CHECK([ovs-appctl ofproto/trace br0 "tun_metadata0=0x1,tun_metadata1=0x2,in_port=br0" | grep conj\\. | sort], [0], [dnl + -> conj. priority=5,tun_metadata0=0x1 + -> conj. priority=5,tun_metadata1=0x2 +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([conjunctive match with or without port map]) +OVS_VSWITCHD_START +add_of_ports br0 1 2 +AT_DATA([flows.txt], [dnl +conj_id=1,actions=drop +conj_id=2,actions=drop + +priority=10,ip,actions=conjunction(1,1/2),conjunction(2,1/2) +priority=10,in_port=p1,actions=conjunction(1,2/2) +priority=10,in_port=p2,actions=conjunction(1,2/2) +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +AT_CHECK([ovs-appctl ofproto/trace br0 "ip,in_port=p1" --names | grep conj\\. | sort], [0], [dnl + -> conj. priority=10,in_port=p1 + -> conj. priority=10,ip +]) +AT_CHECK([ovs-appctl ofproto/trace br0 "ip,in_port=p2" | grep conj\\. | sort], [0], [dnl + -> conj. priority=10,in_port=2 + -> conj. priority=10,ip +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + +AT_SETUP([conjunctive match with resubmit]) +OVS_VSWITCHD_START +add_of_ports br0 1 2 +AT_DATA([flows.txt], [dnl +conj_id=1,actions=resubmit(,2) +priority=10,ip,actions=conjunction(1,1/2) +priority=10,in_port=p1,actions=conjunction(1,2/2) +priority=10,in_port=p2,actions=conjunction(1,2/2) + +table=2,conj_id=7,actions=resubmit(,3) +table=2,priority=20,ip,actions=conjunction(7,1/2) +table=2,priority=20,in_port=p1,actions=conjunction(7,2/2) +table=2,priority=20,in_port=p2,actions=conjunction(7,2/2) + +table=3,actions=drop +]) +AT_CHECK([ovs-ofctl add-flows br0 flows.txt]) +# Check that conj_flows are reset for each table and that they are output +# exactly once. +AT_CHECK([ovs-appctl ofproto/trace br0 "ip,in_port=p1" --names | grep conj\\. | sort], [0], [dnl + -> conj. priority=10,in_port=p1 + -> conj. priority=10,ip + -> conj. priority=20,in_port=p1 + -> conj. priority=20,ip +]) +OVS_VSWITCHD_STOP +AT_CLEANUP + # Flow classifier a packet with excess of padding. AT_SETUP([flow classifier - packet with extra padding]) OVS_VSWITCHD_START diff --git a/tests/test-classifier.c b/tests/test-classifier.c index cff00c8fa35..2c1604a01e2 100644 --- a/tests/test-classifier.c +++ b/tests/test-classifier.c @@ -441,7 +441,7 @@ compare_classifiers(struct classifier *cls, size_t n_invisible_rules, /* This assertion is here to suppress a GCC 4.9 array-bounds warning */ ovs_assert(cls->n_tries <= CLS_MAX_TRIES); - cr0 = classifier_lookup(cls, version, &flow, &wc); + cr0 = classifier_lookup(cls, version, &flow, &wc, NULL); cr1 = tcls_lookup(tcls, &flow); assert((cr0 == NULL) == (cr1 == NULL)); if (cr0 != NULL) { @@ -454,7 +454,7 @@ compare_classifiers(struct classifier *cls, size_t n_invisible_rules, /* Make sure the rule should have been visible. */ assert(cls_rule_visible_in_version(cr0, version)); } - cr2 = classifier_lookup(cls, version, &flow, NULL); + cr2 = classifier_lookup(cls, version, &flow, NULL, NULL); assert(cr2 == cr0); } } @@ -1370,10 +1370,10 @@ lookup_classifier(void *aux_) if (aux->use_wc) { flow_wildcards_init_catchall(&wc); cr = classifier_lookup(aux->cls, version, &aux->lookup_flows[x], - &wc); + &wc, NULL); } else { cr = classifier_lookup(aux->cls, version, &aux->lookup_flows[x], - NULL); + NULL, NULL); } if (cr) { hits++;