From 5a0a5702555d22f9a4d86739455516723cd5ba08 Mon Sep 17 00:00:00 2001 From: Gurucharan Shetty Date: Sun, 13 Oct 2013 12:44:20 -0700 Subject: [PATCH] ovs-dpctl: Add a 'filter' option to match wildcarded 'dump-flows'. With mega-flows, many flows in the kernel datapath are wildcarded. For someone that is debugging a system and wants to find a particular flow and its actions, it is a little hard to zero-in on the flow because some fields are wildcarded. With the filter='$filter' option, we can now filter on the o/p of 'ovs-dpctl dump-flows'. Signed-off-by: Gurucharan Shetty Acked-by: Ben Pfaff --- lib/meta-flow.c | 18 +++++++++ lib/meta-flow.h | 1 + lib/ofp-parse.c | 49 +++++++++++++++++++----- lib/ofp-parse.h | 4 +- ofproto/ofproto-dpif.c | 2 +- tests/odp.at | 65 +++++++++++++++++++++++++++++++ tests/test-odp.c | 83 ++++++++++++++++++++++++++++++++++++++++ utilities/ovs-dpctl.8.in | 10 ++++- utilities/ovs-dpctl.c | 47 ++++++++++++++++++++++- 9 files changed, 264 insertions(+), 15 deletions(-) diff --git a/lib/meta-flow.c b/lib/meta-flow.c index 3ac396f37..12811ef07 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -1538,6 +1538,24 @@ mf_set_value(const struct mf_field *mf, } } +/* Unwildcard 'mask' member field described by 'mf'. The caller is + * responsible for ensuring that 'mask' meets 'mf''s prerequisites. */ +void +mf_mask_field(const struct mf_field *mf, struct flow *mask) +{ + static const union mf_value exact_match_mask = MF_EXACT_MASK_INITIALIZER; + + /* For MFF_DL_VLAN, we cannot send a all 1's to flow_set_dl_vlan() + * as that will be considered as OFP10_VLAN_NONE. So consider it as a + * special case. For the rest, calling mf_set_flow_value() is good + * enough. */ + if (mf->id == MFF_DL_VLAN) { + flow_set_dl_vlan(mask, htons(VLAN_VID_MASK)); + } else { + mf_set_flow_value(mf, &exact_match_mask, mask); + } +} + /* Sets 'flow' member field described by 'mf' to 'value'. The caller is * responsible for ensuring that 'flow' meets 'mf''s prerequisites.*/ void diff --git a/lib/meta-flow.h b/lib/meta-flow.h index a3f6701fe..d3185e4e6 100644 --- a/lib/meta-flow.h +++ b/lib/meta-flow.h @@ -355,6 +355,7 @@ void mf_set_value(const struct mf_field *, const union mf_value *value, void mf_set_flow_value(const struct mf_field *, const union mf_value *value, struct flow *); bool mf_is_zero(const struct mf_field *, const struct flow *); +void mf_mask_field(const struct mf_field *, struct flow *); void mf_get(const struct mf_field *, const struct match *, union mf_value *value, union mf_value *mask); diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c index 2350de303..17bd7e2cc 100644 --- a/lib/ofp-parse.c +++ b/lib/ofp-parse.c @@ -36,6 +36,7 @@ #include "openflow/openflow.h" #include "ovs-thread.h" #include "packets.h" +#include "simap.h" #include "socket-util.h" #include "vconn.h" @@ -1879,18 +1880,24 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr, /* Parses a specification of a flow from 's' into 'flow'. 's' must take the * form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a * mf_field. Fields must be specified in a natural order for satisfying - * prerequisites. + * prerequisites. If 'mask' is specified, fills the mask field for each of the + * field specified in flow. If the map, 'names_portno' is specfied, converts + * the in_port name into port no while setting the 'flow'. * * Returns NULL on success, otherwise a malloc()'d string that explains the * problem. */ char * -parse_ofp_exact_flow(struct flow *flow, const char *s) +parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s, + const struct simap *portno_names) { char *pos, *key, *value_s; char *error = NULL; char *copy; memset(flow, 0, sizeof *flow); + if (mask) { + memset(mask, 0, sizeof *mask); + } pos = copy = xstrdup(s); while (ofputil_parse_key_value(&pos, &key, &value_s)) { @@ -1901,6 +1908,9 @@ parse_ofp_exact_flow(struct flow *flow, const char *s) goto exit; } flow->dl_type = htons(p->dl_type); + if (mask) { + mask->dl_type = OVS_BE16_MAX; + } if (p->nw_proto) { if (flow->nw_proto) { @@ -1909,6 +1919,9 @@ parse_ofp_exact_flow(struct flow *flow, const char *s) goto exit; } flow->nw_proto = p->nw_proto; + if (mask) { + mask->nw_proto = UINT8_MAX; + } } } else { const struct mf_field *mf; @@ -1932,15 +1945,28 @@ parse_ofp_exact_flow(struct flow *flow, const char *s) goto exit; } - field_error = mf_parse_value(mf, value_s, &value); - if (field_error) { - error = xasprintf("%s: bad value for %s (%s)", - s, key, field_error); - free(field_error); - goto exit; - } + if (!strcmp(key, "in_port") + && portno_names + && simap_contains(portno_names, value_s)) { + flow->in_port.ofp_port = u16_to_ofp( + simap_get(portno_names, value_s)); + if (mask) { + mask->in_port.ofp_port = u16_to_ofp(ntohs(OVS_BE16_MAX)); + } + } else { + field_error = mf_parse_value(mf, value_s, &value); + if (field_error) { + error = xasprintf("%s: bad value for %s (%s)", + s, key, field_error); + free(field_error); + goto exit; + } - mf_set_flow_value(mf, &value, flow); + mf_set_flow_value(mf, &value, flow); + if (mask) { + mf_mask_field(mf, mask); + } + } } } @@ -1953,6 +1979,9 @@ exit: if (error) { memset(flow, 0, sizeof *flow); + if (mask) { + memset(mask, 0, sizeof *mask); + } } return error; } diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h index 47ba036ec..515ccd702 100644 --- a/lib/ofp-parse.h +++ b/lib/ofp-parse.h @@ -32,6 +32,7 @@ struct ofputil_flow_stats_request; struct ofputil_group_mod; struct ofputil_meter_mod; struct ofputil_table_mod; +struct simap; enum ofputil_protocol; char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_, @@ -62,7 +63,8 @@ char *parse_ofpacts(const char *, struct ofpbuf *ofpacts, enum ofputil_protocol *usable_protocols) WARN_UNUSED_RESULT; -char *parse_ofp_exact_flow(struct flow *, const char *); +char *parse_ofp_exact_flow(struct flow *flow, struct flow *mask, const char *s, + const struct simap *portno_names); char *parse_ofp_meter_mod_str(struct ofputil_meter_mod *, const char *string, int command, diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index 8ef1c8cb0..030572c89 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -5201,7 +5201,7 @@ ofproto_unixctl_trace(struct unixctl_conn *conn, int argc, const char *argv[], goto exit; } ds_put_format(&result, "Bridge: %s\n", ofproto->up.name); - } else if (!parse_ofp_exact_flow(&flow, argv[argc - 1])) { + } else if (!parse_ofp_exact_flow(&flow, NULL, argv[argc - 1], NULL)) { if (argc != 3) { unixctl_command_reply_error(conn, "Must specify bridge name"); goto exit; diff --git a/tests/odp.at b/tests/odp.at index 469e120dd..b50534512 100644 --- a/tests/odp.at +++ b/tests/odp.at @@ -151,6 +151,71 @@ AT_CHECK_UNQUOTED([test-odp parse-wc-keys < odp.txt], [0], [`cat odp.txt` ]) AT_CLEANUP +AT_SETUP([OVS datapath wildcarded key filtering.]) +dnl We could add a test for invalid forms, but that's less important. +AT_DATA([odp-base.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234/0xfff0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no/0xf0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff,dst=6632/0xff00) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=1,tos=0,ttl=128,frag=no),icmp(type=1/0xf0,code=2/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1/::255,dst=::2/::255,label=0/0xf0,proto=10/0xf0,tclass=0x70/0xf0,hlimit=128/0xf0,frag=no/0xf0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00) +]) +AT_DATA([odp-vlan-base.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=100,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff)) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=100,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff)) +]) +AT_DATA([odp-eth-type.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x1234/0xfff0) +]) +AT_DATA([odp-vlan.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff)) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=17,tos=0,ttl=128,frag=no),udp(src=81/0xff00,dst=6632/0xff)) +]) +AT_DATA([odp-ipv4.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no/0xf0) +]) +AT_DATA([odp-icmp.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no/0xf0) +]) +AT_DATA([odp-arp.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0806),arp(sip=1.2.3.4/255.255.255.250,tip=5.6.7.8/255.255.255.250,op=1/0xf0,sha=00:0f:10:11:12:13/ff:ff:ff:ff:ff:00,tha=00:14:15:16:17:18/ff:ff:ff:ff:ff:00) +]) +AT_DATA([odp-tcp.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41/255.255.255.0,dst=172.16.0.20/255.255.255.0,proto=5/0xf0,tos=0x80/0xf0,ttl=128/0xf0,frag=no/0xf0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x0800),ipv4(src=35.8.2.41,dst=172.16.0.20,proto=6,tos=0,ttl=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) +]) +AT_DATA([odp-tcp6.txt], [dnl +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1/::255,dst=::2/::255,label=0/0xf0,proto=10/0xf0,tclass=0x70/0xf0,hlimit=128/0xf0,frag=no/0xf0) +in_port(1),eth(src=00:01:02:03:04:05,dst=10:11:12:13:14:15),eth_type(0x86dd),ipv6(src=::1,dst=::2,label=0,proto=6,tclass=0,hlimit=128,frag=no),tcp(src=80/0xff00,dst=8080/0xff) +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='dl_type=0x1235' < odp-base.txt], [0], [`cat odp-eth-type.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='dl_vlan=99' < odp-vlan-base.txt], [0], [`cat odp-vlan.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='dl_vlan=99,ip' < odp-vlan-base.txt], [0], [`cat odp-vlan.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='ip,nw_src=35.8.2.199' < odp-base.txt], [0], [`cat odp-ipv4.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='ip,nw_dst=172.16.0.199' < odp-base.txt], [0], [`cat odp-ipv4.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='dl_type=0x0800,nw_src=35.8.2.199,nw_dst=172.16.0.199' < odp-base.txt], [0], [`cat odp-ipv4.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='icmp,nw_src=35.8.2.199' < odp-base.txt], [0], [`cat odp-icmp.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='arp,arp_spa=1.2.3.5' < odp-base.txt], [0], [`cat odp-arp.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='tcp,tp_src=90' < odp-base.txt], [0], [`cat odp-tcp.txt` +]) +AT_CHECK_UNQUOTED([test-odp parse-filter filter='tcp6,tp_src=90' < odp-base.txt], [0], [`cat odp-tcp6.txt` +]) +AT_CLEANUP + AT_SETUP([OVS datapath actions parsing and formatting - valid forms]) AT_DATA([actions.txt], [dnl 1,2,3 diff --git a/tests/test-odp.c b/tests/test-odp.c index 183a3b3cd..471851b29 100644 --- a/tests/test-odp.c +++ b/tests/test-odp.c @@ -20,7 +20,9 @@ #include "dynamic-string.h" #include "flow.h" +#include "match.h" #include "odp-util.h" +#include "ofp-parse.h" #include "ofpbuf.h" #include "util.h" #include "vlog.h" @@ -135,15 +137,96 @@ parse_actions(void) return 0; } +static int +parse_filter(char *filter_parse) +{ + struct ds in; + struct flow flow_filter; + struct flow_wildcards wc_filter; + char *error, *filter = NULL; + + vlog_set_levels_from_string_assert("odp_util:console:dbg"); + if (filter_parse && !strncmp(filter_parse, "filter=", 7)) { + filter = strdup(filter_parse+7); + memset(&flow_filter, 0, sizeof(flow_filter)); + memset(&wc_filter, 0, sizeof(wc_filter)); + + error = parse_ofp_exact_flow(&flow_filter, &wc_filter.masks, filter, + NULL); + if (error) { + ovs_fatal(0, "Failed to parse filter (%s)", error); + } + } else { + ovs_fatal(0, "No filter to parse."); + } + + ds_init(&in); + while (!ds_get_test_line(&in, stdin)) { + struct ofpbuf odp_key; + struct ofpbuf odp_mask; + struct ds out; + int error; + + /* Convert string to OVS DP key. */ + ofpbuf_init(&odp_key, 0); + ofpbuf_init(&odp_mask, 0); + error = odp_flow_from_string(ds_cstr(&in), NULL, + &odp_key, &odp_mask); + if (error) { + printf("odp_flow_from_string: error\n"); + goto next; + } + + if (filter) { + struct flow flow; + struct flow_wildcards wc; + struct match match, match_filter; + struct minimatch minimatch; + + odp_flow_key_to_flow(odp_key.data, odp_key.size, &flow); + odp_flow_key_to_mask(odp_mask.data, odp_mask.size, &wc.masks, + &flow); + match_init(&match, &flow, &wc); + + match_init(&match_filter, &flow_filter, &wc); + match_init(&match_filter, &match_filter.flow, &wc_filter); + minimatch_init(&minimatch, &match_filter); + + if (!minimatch_matches_flow(&minimatch, &match.flow)) { + minimatch_destroy(&minimatch); + goto next; + } + minimatch_destroy(&minimatch); + } + /* Convert odp_key to string. */ + ds_init(&out); + odp_flow_format(odp_key.data, odp_key.size, + odp_mask.data, odp_mask.size, NULL, &out, false); + puts(ds_cstr(&out)); + ds_destroy(&out); + + next: + ofpbuf_uninit(&odp_key); + ofpbuf_uninit(&odp_mask); + } + ds_destroy(&in); + + free(filter); + return 0; +} + int main(int argc, char *argv[]) { + set_program_name(argv[0]); if (argc == 2 &&!strcmp(argv[1], "parse-keys")) { return parse_keys(false); } else if (argc == 2 &&!strcmp(argv[1], "parse-wc-keys")) { return parse_keys(true); } else if (argc == 2 && !strcmp(argv[1], "parse-actions")) { return parse_actions(); + } else if (argc == 3 && !strcmp(argv[1], "parse-filter")) { + return parse_filter(argv[2]); } else { ovs_fatal(0, "usage: %s parse-keys | parse-wc-keys | parse-actions", argv[0]); } diff --git a/utilities/ovs-dpctl.8.in b/utilities/ovs-dpctl.8.in index 5c0157028..35d1be590 100644 --- a/utilities/ovs-dpctl.8.in +++ b/utilities/ovs-dpctl.8.in @@ -118,11 +118,19 @@ exactly one datapath exists, in which case that datapath is the default. When multiple datapaths exist, then a datapath name is required. . -.IP "[\fB\-m \fR| \fB\-\-more\fR] \fBdump\-flows\fR [\fIdp\fR]" +.IP "[\fB\-m \fR| \fB\-\-more\fR] \fBdump\-flows\fR [\fIdp\fR] [\fBfilter=\fIfilter\fR]" Prints to the console all flow entries in datapath \fIdp\fR's flow table. Without \fB\-m\fR or \fB\-\-more\fR, output omits match fields that a flow wildcards entirely; with \fB\-m\fR or \fB\-\-more\fR, output includes all wildcarded fields. +.IP +If \fBfilter=\fIfilter\fR is specified, only displays the flows +that match the \fIfilter\fR. \fIfilter\fR is a flow in the form similiar +to that accepted by \fBovs\-ofctl\fR(8)'s \fBadd\-flow\fR command. (This is +not an OpenFlow flow: besides other differences, it never contains wildcards.) +The \fIfilter\fR is also useful to match wildcarded fields in the datapath +flow. As an example, \fBfilter='tcp,tp_src=100'\fR will match the +datapath flow containing '\fBtcp(src=80/0xff00,dst=8080/0xff)\fR'. . .IP "\fBadd\-flow\fR [\fIdp\fR] \fIflow actions\fR" .IQ "[\fB\-\-clear\fR] [\fB\-\-may-create\fR] [\fB\-s\fR | \fB\-\-statistics\fR] \fBmod\-flow\fR [\fIdp\fR] \fIflow actions\fR" diff --git a/utilities/ovs-dpctl.c b/utilities/ovs-dpctl.c index 4fb02dda4..e11e102e3 100644 --- a/utilities/ovs-dpctl.c +++ b/utilities/ovs-dpctl.c @@ -36,9 +36,11 @@ #include "dpif.h" #include "dynamic-string.h" #include "flow.h" +#include "match.h" #include "netdev.h" #include "netlink.h" #include "odp-util.h" +#include "ofp-parse.h" #include "ofpbuf.h" #include "packets.h" #include "shash.h" @@ -746,20 +748,38 @@ dpctl_dump_flows(int argc, char *argv[]) struct dpif_port dpif_port; struct dpif_port_dump port_dump; struct hmap portno_names; + struct simap names_portno; size_t actions_len; struct dpif *dpif; size_t key_len; size_t mask_len; struct ds ds; - char *name; + char *name, *error, *filter = NULL; + struct flow flow_filter; + struct flow_wildcards wc_filter; + if (argc > 1 && !strncmp(argv[argc - 1], "filter=", 7)) { + filter = xstrdup(argv[--argc] + 7); + } name = (argc == 2) ? xstrdup(argv[1]) : get_one_dp(); + run(parsed_dpif_open(name, false, &dpif), "opening datapath"); free(name); hmap_init(&portno_names); + simap_init(&names_portno); DPIF_PORT_FOR_EACH (&dpif_port, &port_dump, dpif) { odp_portno_names_set(&portno_names, dpif_port.port_no, dpif_port.name); + simap_put(&names_portno, dpif_port.name, + odp_to_u32(dpif_port.port_no)); + } + + if (filter) { + error = parse_ofp_exact_flow(&flow_filter, &wc_filter.masks, filter, + &names_portno); + if (error) { + ovs_fatal(0, "Failed to parse filter (%s)", error); + } } ds_init(&ds); @@ -767,6 +787,26 @@ dpctl_dump_flows(int argc, char *argv[]) while (dpif_flow_dump_next(&flow_dump, &key, &key_len, &mask, &mask_len, &actions, &actions_len, &stats)) { + if (filter) { + struct flow flow; + struct flow_wildcards wc; + struct match match, match_filter; + struct minimatch minimatch; + + odp_flow_key_to_flow(key, key_len, &flow); + odp_flow_key_to_mask(mask, mask_len, &wc.masks, &flow); + match_init(&match, &flow, &wc); + + match_init(&match_filter, &flow_filter, &wc); + match_init(&match_filter, &match_filter.flow, &wc_filter); + minimatch_init(&minimatch, &match_filter); + + if (!minimatch_matches_flow(&minimatch, &match.flow)) { + minimatch_destroy(&minimatch); + continue; + } + minimatch_destroy(&minimatch); + } ds_clear(&ds); odp_flow_format(key, key_len, mask, mask_len, &portno_names, &ds, verbosity); @@ -778,8 +818,11 @@ dpctl_dump_flows(int argc, char *argv[]) printf("%s\n", ds_cstr(&ds)); } dpif_flow_dump_done(&flow_dump); + + free(filter); odp_portno_names_destroy(&portno_names); hmap_destroy(&portno_names); + simap_destroy(&names_portno); ds_destroy(&ds); dpif_close(dpif); } @@ -1166,7 +1209,7 @@ static const struct command all_commands[] = { { "set-if", 2, INT_MAX, dpctl_set_if }, { "dump-dps", 0, 0, dpctl_dump_dps }, { "show", 0, INT_MAX, dpctl_show }, - { "dump-flows", 0, 1, dpctl_dump_flows }, + { "dump-flows", 0, 2, dpctl_dump_flows }, { "add-flow", 2, 3, dpctl_add_flow }, { "mod-flow", 2, 3, dpctl_mod_flow }, { "del-flow", 1, 2, dpctl_del_flow }, -- 2.43.0