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 <gshe...@nicira.com>
---
 lib/meta-flow.c          |  191 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/meta-flow.h          |    1 +
 lib/ofp-parse.c          |   39 +++++++++-
 lib/ofp-parse.h          |    4 +-
 ofproto/ofproto-dpif.c   |    2 +-
 tests/odp.at             |   65 ++++++++++++++++
 tests/test-odp.c         |   85 +++++++++++++++++++++
 utilities/ovs-dpctl.8.in |   10 ++-
 utilities/ovs-dpctl.c    |   60 ++++++++++++++-
 9 files changed, 448 insertions(+), 9 deletions(-)

diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 3a31c29..3fb8ea1 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -1494,6 +1494,197 @@ mf_set_value(const struct mf_field *mf,
     }
 }
 
+/* Unwildcard 'mask' member field described by 'mf'.  The caller is
+ * responsible for ensuring that 'flow' meets 'mf''s prerequisites.*/
+void
+mf_set_flow_mask(const struct mf_field *mf, struct flow *mask)
+{
+    switch (mf->id) {
+    case MFF_TUN_ID:
+        mask->tunnel.tun_id = OVS_BE64_MAX;
+        break;
+    case MFF_TUN_SRC:
+        mask->tunnel.ip_src = OVS_BE32_MAX;
+        break;
+    case MFF_TUN_DST:
+        mask->tunnel.ip_dst = OVS_BE32_MAX;
+        break;
+    case MFF_TUN_FLAGS:
+        mask->tunnel.flags = UINT16_MAX;
+        break;
+    case MFF_TUN_TOS:
+        mask->tunnel.ip_tos = UINT8_MAX;
+        break;
+    case MFF_TUN_TTL:
+        mask->tunnel.ip_ttl = UINT8_MAX;
+        break;
+
+    case MFF_METADATA:
+        mask->metadata = OVS_BE64_MAX;
+        break;
+
+    case MFF_IN_PORT:
+        mask->in_port.ofp_port = u16_to_ofp(UINT16_MAX);
+        break;
+
+    case MFF_IN_PORT_OXM: {
+        ofp_port_t port;
+        ofputil_port_from_ofp11(OVS_BE32_MAX, &port);
+        mask->in_port.ofp_port = port;
+        break;
+    }
+
+    case MFF_SKB_PRIORITY:
+        mask->skb_priority = UINT32_MAX;
+        break;
+
+    case MFF_PKT_MARK:
+        mask->pkt_mark = UINT32_MAX;
+        break;
+
+    CASE_MFF_REGS:
+        mask->regs[mf->id - MFF_REG0] = UINT32_MAX;
+        break;
+
+    case MFF_ETH_SRC:
+        memset(&mask->dl_src, 0xff, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_DST:
+        memset(&mask->dl_dst, 0xff, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ETH_TYPE:
+        mask->dl_type = OVS_BE16_MAX;
+        break;
+
+    case MFF_VLAN_TCI:
+        mask->vlan_tci = OVS_BE16_MAX;
+        break;
+
+    case MFF_DL_VLAN:
+        flow_set_dl_vlan(mask, htons(VLAN_VID_MASK));
+        break;
+
+    case MFF_VLAN_VID:
+        flow_set_vlan_vid(mask, htons(VLAN_VID_MASK));
+        break;
+
+    case MFF_DL_VLAN_PCP:
+    case MFF_VLAN_PCP:
+        flow_set_vlan_pcp(mask, UINT8_MAX);
+        break;
+
+    case MFF_MPLS_LABEL:
+        flow_set_mpls_label(mask, OVS_BE32_MAX);
+        break;
+
+    case MFF_MPLS_TC:
+        flow_set_mpls_tc(mask, UINT8_MAX);
+        break;
+
+    case MFF_MPLS_BOS:
+        flow_set_mpls_bos(mask, UINT8_MAX);
+        break;
+
+    case MFF_IPV4_SRC:
+        mask->nw_src = OVS_BE32_MAX;
+        break;
+
+    case MFF_IPV4_DST:
+        mask->nw_dst = OVS_BE32_MAX;
+        break;
+
+    case MFF_IPV6_SRC:
+        memset(&mask->ipv6_src, 0xff, sizeof mask->ipv6_src);
+        break;
+
+    case MFF_IPV6_DST:
+        memset(&mask->ipv6_dst, 0xff, sizeof mask->ipv6_dst);
+        break;
+
+    case MFF_IPV6_LABEL:
+        mask->ipv6_label = OVS_BE32_MAX;
+        break;
+
+    case MFF_IP_PROTO:
+        mask->nw_proto = UINT8_MAX;
+        break;
+
+    case MFF_IP_DSCP:
+        mask->nw_tos |= IP_DSCP_MASK;
+        break;
+
+    case MFF_IP_DSCP_SHIFTED:
+        mask->nw_tos |= IP_DSCP_MASK;
+        break;
+
+    case MFF_IP_ECN:
+        mask->nw_tos |= IP_ECN_MASK;
+        break;
+
+    case MFF_IP_TTL:
+        mask->nw_ttl = UINT8_MAX;
+        break;
+
+    case MFF_IP_FRAG:
+        mask->nw_frag |= FLOW_NW_FRAG_MASK;
+        break;
+
+    case MFF_ARP_OP:
+        mask->nw_proto = UINT8_MAX;
+        break;
+
+    case MFF_ARP_SPA:
+        mask->nw_src = OVS_BE32_MAX;
+        break;
+
+    case MFF_ARP_TPA:
+        mask->nw_dst = OVS_BE32_MAX;
+        break;
+
+    case MFF_ARP_SHA:
+    case MFF_ND_SLL:
+        memset(&mask->arp_sha, 0xff, ETH_ADDR_LEN);
+        break;
+
+    case MFF_ARP_THA:
+    case MFF_ND_TLL:
+        memset(&mask->arp_tha, 0xff, ETH_ADDR_LEN);
+        break;
+
+    case MFF_TCP_SRC:
+    case MFF_UDP_SRC:
+    case MFF_SCTP_SRC:
+        mask->tp_src = OVS_BE16_MAX;
+        break;
+
+    case MFF_TCP_DST:
+    case MFF_UDP_DST:
+    case MFF_SCTP_DST:
+        mask->tp_dst = OVS_BE16_MAX;
+        break;
+
+    case MFF_ICMPV4_TYPE:
+    case MFF_ICMPV6_TYPE:
+        mask->tp_src = htons(UINT8_MAX);
+        break;
+
+    case MFF_ICMPV4_CODE:
+    case MFF_ICMPV6_CODE:
+        mask->tp_dst = htons(UINT8_MAX);
+        break;
+
+    case MFF_ND_TARGET:
+        memset(&mask->nd_target, 0xff, sizeof mask->nd_target);
+        break;
+
+    case MFF_N_IDS:
+    default:
+        NOT_REACHED();
+    }
+}
+
 /* 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 dd8b95d..80e9b8a 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -352,6 +352,7 @@ void mf_set_value(const struct mf_field *, const union 
mf_value *value,
                   struct match *);
 void mf_set_flow_value(const struct mf_field *, const union mf_value *value,
                        struct flow *);
+void mf_set_flow_mask(const struct mf_field *, struct flow *);
 bool mf_is_zero(const struct mf_field *, const struct flow *);
 
 void mf_get(const struct mf_field *, const struct match *,
diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
index 7ca7305..e9abdc4 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"
 
@@ -1845,18 +1846,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 *pos, *key, *value_s, *value_inport = NULL;
     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)) {
@@ -1867,6 +1874,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) {
@@ -1875,6 +1885,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;
@@ -1898,6 +1911,15 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
                 goto exit;
             }
 
+            if (!strcmp(key, "in_port") && isalpha(*value_s) && portno_names) {
+                const struct simap_node *node;
+                node = simap_find(portno_names, value_s);
+                if (node) {
+                    value_inport = xasprintf("%d", node->data);
+                    value_s = value_inport;
+                }
+            }
+
             field_error = mf_parse_value(mf, value_s, &value);
             if (field_error) {
                 error = xasprintf("%s: bad value for %s (%s)",
@@ -1907,6 +1929,14 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
             }
 
             mf_set_flow_value(mf, &value, flow);
+            if (mask) {
+                mf_set_flow_mask(mf, mask);
+            }
+
+            if (value_inport) {
+                free(value_inport);
+                value_inport = NULL;
+            }
         }
     }
 
@@ -1919,6 +1949,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 47ba036..515ccd7 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 80e97e0..a4e8f00 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -5295,7 +5295,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 469e120..b505345 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 183a3b3..af3b06f 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,6 +137,87 @@ 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) {
+            printf("Failed to parse filter (%s).\n", error);
+            free(error);
+            exit(1);
+        }
+    } else {
+            printf("No filter to parse\n.");
+            exit(1);
+    }
+
+    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[])
 {
@@ -144,6 +227,8 @@ main(int argc, char *argv[])
         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 5c01570..c8345a5 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\fR=\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\fR=\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\fR='\fBtcp,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 4fb02dd..0c73b5d 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,49 @@ 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) {
+        name = get_one_dp();
+    } else if (argc == 2) {
+        if (!strncmp(argv[1], "filter=", 7)) {
+            filter = xstrdup(argv[1]+7);
+            name = get_one_dp();
+        } else {
+            name = xstrdup(argv[1]);
+        }
+    } else {
+        name = xstrdup(argv[1]);
+        filter = xstrdup(argv[2]+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) {
+            printf("Failed to parse filter (%s).\n", error);
+            exit(1);
+        }
     }
 
     ds_init(&ds);
@@ -767,6 +798,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 +829,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 +1220,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 },
-- 
1.7.9.5

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to