diff mbox series

[RFC,net-next,3/6] selftests: openvswitch: add flow dump support

Message ID 20221122140307.705112-4-aconole@redhat.com (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series Allow excluding sw flow key from upcalls | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 827 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Aaron Conole Nov. 22, 2022, 2:03 p.m. UTC
Add a basic set of fields to print in a 'dpflow' format.  This will be
used by future commits to check for flow fields after parsing, as
well as verifying the flow fields pushed into the kernel from
userspace.

Signed-off-by: Aaron Conole <aconole@redhat.com>
---
 .../selftests/net/openvswitch/ovs-dpctl.py    | 781 +++++++++++++++++-
 1 file changed, 780 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index 338e9b2cd660..d654fe1fe4e6 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -6,12 +6,16 @@ 
 
 import argparse
 import errno
+import ipaddress
+import logging
 import sys
+import time
 
 try:
     from pyroute2 import NDB
 
     from pyroute2.netlink import NLM_F_ACK
+    from pyroute2.netlink import NLM_F_DUMP
     from pyroute2.netlink import NLM_F_REQUEST
     from pyroute2.netlink import genlmsg
     from pyroute2.netlink import nla
@@ -40,6 +44,11 @@  OVS_VPORT_CMD_DEL = 2
 OVS_VPORT_CMD_GET = 3
 OVS_VPORT_CMD_SET = 4
 
+OVS_FLOW_CMD_NEW = 1
+OVS_FLOW_CMD_DEL = 2
+OVS_FLOW_CMD_GET = 3
+OVS_FLOW_CMD_SET = 4
+
 
 class ovs_dp_msg(genlmsg):
     # include the OVS version
@@ -302,6 +311,760 @@  class OvsVport(GenericNetlinkSocket):
         return reply
 
 
+def macstr(mac):
+    outstr = ":".join(["%02X" % i for i in mac])
+    return outstr
+
+
+class OvsFlow(GenericNetlinkSocket):
+    class ovs_flow_msg(ovs_dp_msg):
+        nla_map = (
+            ("OVS_FLOW_ATTR_UNSPEC", "none"),
+            ("OVS_FLOW_ATTR_KEY", "nested"),
+            ("OVS_FLOW_ATTR_ACTIONS", "nested"),
+            ("OVS_FLOW_ATTR_STATS", "flowstats"),
+            ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"),
+            ("OVS_FLOW_ATTR_USED", "uint64"),
+            ("OVS_FLOW_ATTR_CLEAR", "none"),
+            ("OVS_FLOW_ATTR_MASK", "nested"),
+            ("OVS_FLOW_ATTR_PROBE", "none"),
+            ("OVS_FLOW_ATTR_UFID", "array(uint32)"),
+            ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"),
+        )
+
+        class nestedacts(nla):
+            __slots__ = ()
+
+            nla_map = ()
+
+        class flowstats(nla):
+            fields = (
+                ("packets", "=Q"),
+                ("bytes", "=Q"),
+            )
+
+        class nestedflow(nla):
+            nla_map = (
+                ("OVS_KEY_ATTR_UNSPEC", "none"),
+                ("OVS_KEY_ATTR_ENCAP", "none"),
+                ("OVS_KEY_ATTR_PRIORITY", "uint32"),
+                ("OVS_KEY_ATTR_IN_PORT", "uint32"),
+                ("OVS_KEY_ATTR_ETHERNET", "ethaddr"),
+                ("OVS_KEY_ATTR_VLAN", "uint16"),
+                ("OVS_KEY_ATTR_ETHERTYPE", "be16"),
+                ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"),
+                ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"),
+                ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"),
+                ("OVS_KEY_ATTR_UDP", "ovs_key_udp"),
+                ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"),
+                ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"),
+                ("OVS_KEY_ATTR_ARP", "ovs_key_arp"),
+                ("OVS_KEY_ATTR_ND", "ovs_key_nd"),
+                ("OVS_KEY_ATTR_SKB_MARK", "uint32"),
+                ("OVS_KEY_ATTR_TUNNEL", "none"),
+                ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"),
+                ("OVS_KEY_ATTR_TCP_FLAGS", "uint16"),
+                ("OVS_KEY_ATTR_DP_HASH", "uint32"),
+                ("OVS_KEY_ATTR_RECIRC_ID", "uint32"),
+                ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"),
+                ("OVS_KEY_ATTR_CT_STATE", "uint32"),
+                ("OVS_KEY_ATTR_CT_ZONE", "uint16"),
+                ("OVS_KEY_ATTR_CT_MARK", "uint32"),
+                ("OVS_KEY_ATTR_CT_LABELS", "none"),
+                ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"),
+                ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"),
+                ("OVS_KEY_ATTR_NSH", "none"),
+                ("OVS_KEY_ATTR_PACKET_TYPE", "none"),
+                ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"),
+                ("OVS_KEY_ATTR_TUNNEL_INFO", "none"),
+                ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"),
+            )
+
+            class ovs_key_proto(nla):
+                fields = (
+                    ("src", "!H"),
+                    ("dst", "!H"),
+                )
+
+                fields_map = (
+                    ("src", "src", "%d", int),
+                    ("dst", "dst", "%d", int),
+                )
+
+                def __init__(
+                    self,
+                    protostr,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    self.proto_str = protostr
+                    nla.__init__(
+                        self,
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+                def dpstr(self, masked=None, more=False):
+                    outstr = self.proto_str + "("
+                    first = False
+                    for f in self.fields_map:
+                        if first:
+                            outstr += ","
+                        if masked is None:
+                            outstr += "%s=" % f[0]
+                            if isinstance(f[2], str):
+                                outstr += f[2] % self[f[1]]
+                            else:
+                                outstr += f[2](self[f[1]])
+                            first = True
+                        elif more or f[3](masked[f[1]]) != 0:
+                            outstr += "%s=" % f[0]
+                            if isinstance(f[2], str):
+                                outstr += f[2] % self[f[1]]
+                            else:
+                                outstr += f[2](self[f[1]])
+                            outstr += "/"
+                            if isinstance(f[2], str):
+                                outstr += f[2] % masked[f[1]]
+                            else:
+                                outstr += f[2](masked[f[1]])
+                            first = True
+                    outstr += ")"
+                    return outstr
+
+            class ethaddr(ovs_key_proto):
+                fields = (
+                    ("src", "!6s"),
+                    ("dst", "!6s"),
+                )
+
+                fields_map = (
+                    ("src", "src", macstr, lambda x: int.from_bytes(x, "big")),
+                    ("dst", "dst", macstr, lambda x: int.from_bytes(x, "big")),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "eth",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_ipv4(ovs_key_proto):
+                fields = (
+                    ("src", "!I"),
+                    ("dst", "!I"),
+                    ("proto", "B"),
+                    ("tos", "B"),
+                    ("ttl", "B"),
+                    ("frag", "B"),
+                )
+
+                fields_map = (
+                    (
+                        "src",
+                        "src",
+                        lambda x: str(ipaddress.IPv4Address(x)),
+                        int,
+                    ),
+                    (
+                        "dst",
+                        "dst",
+                        lambda x: str(ipaddress.IPv4Address(x)),
+                        int,
+                    ),
+                    ("proto", "proto", "%d", int),
+                    ("tos", "tos", "%d", int),
+                    ("ttl", "ttl", "%d", int),
+                    ("frag", "frag", "%d", int),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "ipv4",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_ipv6(ovs_key_proto):
+                fields = (
+                    ("src", "!16s"),
+                    ("dst", "!16s"),
+                    ("label", "!I"),
+                    ("proto", "B"),
+                    ("tclass", "B"),
+                    ("hlimit", "B"),
+                    ("frag", "B"),
+                )
+
+                fields_map = (
+                    (
+                        "src",
+                        "src",
+                        lambda x: str(ipaddress.IPv6Address(x)),
+                        lambda x: int.from_bytes(x, "big"),
+                    ),
+                    (
+                        "dst",
+                        "dst",
+                        lambda x: str(ipaddress.IPv6Address(x)),
+                        lambda x: int.from_bytes(x, "big"),
+                    ),
+                    ("label", "label", "%d", int),
+                    ("proto", "proto", "%d", int),
+                    ("tclass", "tclass", "%d", int),
+                    ("hlimit", "hlimit", "%d", int),
+                    ("frag", "frag", "%d", int),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "ipv6",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_tcp(ovs_key_proto):
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "tcp",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_udp(ovs_key_proto):
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "udp",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_sctp(ovs_key_proto):
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "sctp",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_icmp(ovs_key_proto):
+                fields = (
+                    ("type", "B"),
+                    ("code", "B"),
+                )
+
+                fields_map = (
+                    ("type", "type", "%d", int),
+                    ("code", "code", "%d", int),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "icmp",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_icmpv6(ovs_key_icmp):
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "icmpv6",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_arp(ovs_key_proto):
+                fields = (
+                    ("sip", "!I"),
+                    ("tip", "!I"),
+                    ("op", "!H"),
+                    ("sha", "!6s"),
+                    ("tha", "!6s"),
+                    ("pad", "xx"),
+                )
+
+                fields_map = (
+                    (
+                        "sip",
+                        "sip",
+                        lambda x: str(ipaddress.IPv4Address(x)),
+                        int,
+                    ),
+                    (
+                        "tip",
+                        "tip",
+                        lambda x: str(ipaddress.IPv4Address(x)),
+                        int,
+                    ),
+                    ("op", "op", "%d", int),
+                    ("sha", "sha", macstr, lambda x: int.from_bytes(x, "big")),
+                    ("tha", "tha", macstr, lambda x: int.from_bytes(x, "big")),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "arp",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_nd(ovs_key_proto):
+                fields = (
+                    ("target", "!16s"),
+                    ("sll", "!6s"),
+                    ("tll", "!6s"),
+                )
+
+                fields_map = (
+                    (
+                        "target",
+                        "target",
+                        lambda x: str(ipaddress.IPv6Address(x)),
+                        lambda x: int.from_bytes(x, "big"),
+                    ),
+                    ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")),
+                    ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "nd",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_ct_tuple_ipv4(ovs_key_proto):
+                fields = (
+                    ("src", "!I"),
+                    ("dst", "!I"),
+                    ("tp_src", "!H"),
+                    ("tp_dst", "!H"),
+                    ("proto", "B"),
+                )
+
+                fields_map = (
+                    (
+                        "src",
+                        "src",
+                        lambda x: str(ipaddress.IPv4Address(x)),
+                        int,
+                    ),
+                    (
+                        "dst",
+                        "dst",
+                        lambda x: str(ipaddress.IPv6Address(x)),
+                        int,
+                    ),
+                    ("tp_src", "tp_src", "%d", int),
+                    ("tp_dst", "tp_dst", "%d", int),
+                    ("proto", "proto", "%d", int),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "ct_tuple4",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_ct_tuple_ipv6(nla):
+                fields = (
+                    ("src", "!16s"),
+                    ("dst", "!16s"),
+                    ("tp_src", "!H"),
+                    ("tp_dst", "!H"),
+                    ("proto", "B"),
+                )
+
+                fields_map = (
+                    (
+                        "src",
+                        "src",
+                        lambda x: str(ipaddress.IPv6Address(x)),
+                        lambda x: int.from_bytes(x, "big"),
+                    ),
+                    (
+                        "dst",
+                        "dst",
+                        lambda x: str(ipaddress.IPv6Address(x)),
+                        lambda x: int.from_bytes(x, "big"),
+                    ),
+                    ("tp_src", "tp_src", "%d", int),
+                    ("tp_dst", "tp_dst", "%d", int),
+                    ("proto", "proto", "%d", int),
+                )
+
+                def __init__(
+                    self,
+                    data=None,
+                    offset=None,
+                    parent=None,
+                    length=None,
+                    init=None,
+                ):
+                    OvsFlow.ovs_flow_msg.nestedflow.ovs_key_proto.__init__(
+                        self,
+                        "ct_tuple6",
+                        data=data,
+                        offset=offset,
+                        parent=parent,
+                        length=length,
+                        init=init,
+                    )
+
+            class ovs_key_mpls(nla):
+                fields = (("lse", ">I"),)
+
+            def dpstr(self, mask=None, more=False):
+                print_str = ""
+
+                for field in (
+                    (
+                        "OVS_KEY_ATTR_PRIORITY",
+                        "skb_priority",
+                        "%d",
+                        lambda x: False,
+                        True,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_SKB_MARK",
+                        "skb_mark",
+                        "%d",
+                        lambda x: False,
+                        True,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_RECIRC_ID",
+                        "recirc_id",
+                        "0x%08X",
+                        lambda x: False,
+                        True,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_DP_HASH",
+                        "dp_hash",
+                        "0x%08X",
+                        lambda x: False,
+                        True,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_CT_STATE",
+                        "ct_state",
+                        "0x%04x",
+                        lambda x: False,
+                        True,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_CT_ZONE",
+                        "ct_zone",
+                        "0x%04x",
+                        lambda x: False,
+                        True,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_CT_MARK",
+                        "ct_mark",
+                        "0x%08x",
+                        lambda x: False,
+                        True,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4",
+                        None,
+                        None,
+                        False,
+                        False,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6",
+                        None,
+                        None,
+                        False,
+                        False,
+                    ),
+                    (
+                        "OVS_KEY_ATTR_IN_PORT",
+                        "in_port",
+                        "%d",
+                        lambda x: True,
+                        True,
+                    ),
+                    ("OVS_KEY_ATTR_ETHERNET", None, None, False, False),
+                    (
+                        "OVS_KEY_ATTR_ETHERTYPE",
+                        "eth_type",
+                        "0x%04x",
+                        lambda x: int(x) == 0xFFFF,
+                        True,
+                    ),
+                    ("OVS_KEY_ATTR_IPV4", None, None, False, False),
+                    ("OVS_KEY_ATTR_IPV6", None, None, False, False),
+                    ("OVS_KEY_ATTR_ARP", None, None, False, False),
+                    ("OVS_KEY_ATTR_TCP", None, None, False, False),
+                    (
+                        "OVS_KEY_ATTR_TCP_FLAGS",
+                        "tcp_flags",
+                        "0x%04x",
+                        lambda x: False,
+                        True,
+                    ),
+                    ("OVS_KEY_ATTR_UDP", None, None, False, False),
+                    ("OVS_KEY_ATTR_SCTP", None, None, False, False),
+                    ("OVS_KEY_ATTR_ICMP", None, None, False, False),
+                    ("OVS_KEY_ATTR_ICMPV6", None, None, False, False),
+                    ("OVS_KEY_ATTR_ND", None, None, False, False),
+                ):
+                    v = self.get_attr(field[0])
+                    if v is not None:
+                        m = None if mask is None else mask.get_attr(field[0])
+                        if field[4] is False:
+                            print_str += v.dpstr(m, more)
+                            print_str += ","
+                        else:
+                            if m is None or field[3](m):
+                                print_str += field[1] + "("
+                                print_str += field[2] % v
+                                print_str += "),"
+                            elif more or m != 0:
+                                print_str += field[1] + "("
+                                print_str += (
+                                    (field[2] % v) + "/" + (field[2] % m)
+                                )
+                                print_str += "),"
+
+                return print_str
+
+        def dpstr(self, more=False):
+            ufid = self.get_attr("OVS_FLOW_ATTR_UFID")
+            ufid_str = ""
+            if ufid is not None:
+                ufid_str = (
+                    "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(
+                        ufid[0],
+                        ufid[1] >> 16,
+                        ufid[1] & 0xFFFF,
+                        ufid[2] >> 16,
+                        ufid[2] & 0,
+                        ufid[3],
+                    )
+                )
+
+            key_field = self.get_attr("OVS_FLOW_ATTR_KEY")
+            keymsg = None
+            if key_field is not None:
+                keymsg = OvsFlow.ovs_flow_msg.nestedflow(data=key_field)
+                keymsg.decode()
+
+            mask_field = self.get_attr("OVS_FLOW_ATTR_MASK")
+            maskmsg = None
+            if mask_field is not None:
+                maskmsg = OvsFlow.ovs_flow_msg.nestedflow(data=mask_field)
+                maskmsg.decode()
+
+            acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS")
+            actsmsg = None
+            if acts_field is not None:
+                actsmsg = OvsFlow.ovs_flow_msg.nestedacts(data=acts_field)
+                actsmsg.decode()
+
+            print_str = ""
+
+            if more:
+                print_str += ufid_str + ","
+
+            if keymsg is not None:
+                print_str += keymsg.dpstr(maskmsg, more)
+
+            stats = self.get_attr("OVS_FLOW_ATTR_STATS")
+            if stats is None:
+                print_str += " packets:0, bytes:0,"
+            else:
+                print_str += " packets:%d, bytes:%d," % (
+                    stats["packets"],
+                    stats["bytes"],
+                )
+
+            used = self.get_attr("OVS_FLOW_ATTR_USED")
+            print_str += " used:"
+            if used is None:
+                print_str += "never,"
+            else:
+                used_time = int(used)
+                cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC)
+                used_time = (cur_time_sec * 1000 * 1000) - used_time
+                print_str += "{}s,".format(used_time / 1000)
+
+            print_str += " actions:"
+            if actsmsg is None or "attrs" not in actsmsg:
+                print_str += "drop"
+
+            return print_str
+
+    def __init__(self):
+        GenericNetlinkSocket.__init__(self)
+        self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg)
+
+    def dump(self, dpifindex, flowspec=None):
+        """
+        Returns a list of messages containing flows.
+
+        dpifindex should be a valid datapath obtained by calling
+        into the OvsDatapath lookup
+
+        flowpsec is a string which represents a flow in the dpctl
+        format.
+        """
+        msg = OvsFlow.ovs_flow_msg()
+
+        msg["cmd"] = OVS_FLOW_CMD_GET
+        msg["version"] = OVS_DATAPATH_VERSION
+        msg["reserved"] = 0
+        msg["dpifindex"] = dpifindex
+
+        msg_flags = NLM_F_REQUEST | NLM_F_ACK
+        if flowspec is None:
+            msg_flags |= NLM_F_DUMP
+        rep = None
+
+        try:
+            rep = self.nlm_request(
+                msg,
+                msg_type=self.prid,
+                msg_flags=msg_flags,
+            )
+        except NetlinkError as ne:
+            raise ne
+        return rep
+
+
 def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
     dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
     base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
@@ -348,6 +1111,7 @@  def main(argv):
         "--verbose",
         action="count",
         help="Increment 'verbose' output counter.",
+        default=0,
     )
     subparsers = parser.add_subparsers()
 
@@ -389,10 +1153,18 @@  def main(argv):
     delifcmd.add_argument("dpname", help="Datapath Name")
     delifcmd.add_argument("delif", help="Interface name for adding")
 
+    dumpflcmd = subparsers.add_parser("dump-flows")
+    dumpflcmd.add_argument("dumpdp", help="Datapath Name")
+
     args = parser.parse_args()
 
+    if args.verbose > 0:
+        if args.verbose > 1:
+            logging.basicConfig(level=logging.DEBUG)
+
     ovsdp = OvsDatapath()
     ovsvp = OvsVport()
+    ovsflow = OvsFlow()
     ndb = NDB()
 
     if hasattr(args, "showdp"):
@@ -445,7 +1217,14 @@  def main(argv):
         else:
             msg += " failed to remove."
         print(msg)
-
+    elif hasattr(args, "dumpdp"):
+        rep = ovsdp.info(args.dumpdp, 0)
+        if rep is None:
+            print("DP '%s' not found." % args.dumpdp)
+            return 1
+        rep = ovsflow.dump(rep["dpifindex"])
+        for flow in rep:
+            print(flow.dpstr(True if args.verbose > 0 else False))
     return 0