All of lore.kernel.org
 help / color / mirror / Atom feed
From: Aaron Conole <aconole@redhat.com>
To: netdev@vger.kernel.org
Cc: dev@openvswitch.org, Pravin Shelar <pshelar@ovn.org>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Ilya Maximets <i.maximets@ovn.org>,
	shuah@kernel.org, linux-kselftest@vger.kernel.org
Subject: [PATCH net-next 2/3] selftests: openvswitch: add flow dump support
Date: Fri, 14 Apr 2023 09:17:49 -0400	[thread overview]
Message-ID: <20230414131750.4185160-3-aconole@redhat.com> (raw)
In-Reply-To: <20230414131750.4185160-1-aconole@redhat.com>

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    | 1026 +++++++++++++++++
 1 file changed, 1026 insertions(+)

diff --git a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
index 626013dfd020e..21b1b8deda7d9 100644
--- a/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
+++ b/tools/testing/selftests/net/openvswitch/ovs-dpctl.py
@@ -6,15 +6,21 @@
 
 import argparse
 import errno
+import ipaddress
+import logging
 import sys
+import time
 
 try:
     from pyroute2 import NDB
 
+    from pyroute2.netlink import NLA_F_NESTED
     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
+    from pyroute2.netlink import nlmsg_atoms
     from pyroute2.netlink.exceptions import NetlinkError
     from pyroute2.netlink.generic import GenericNetlinkSocket
 except ModuleNotFoundError:
@@ -40,6 +46,36 @@ 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
+
+
+def macstr(mac):
+    outstr = ":".join(["%02X" % i for i in mac])
+    return outstr
+
+
+def convert_mac(mac_str, mask=False):
+    if mac_str is None or mac_str == "":
+        mac_str = "00:00:00:00:00:00"
+    if mask is True and mac_str != "00:00:00:00:00:00":
+        mac_str = "FF:FF:FF:FF:FF:FF"
+    mac_split = mac_str.split(":")
+    ret = bytearray([int(i, 16) for i in mac_split])
+    return bytes(ret)
+
+
+def convert_ipv4(ip, mask=False):
+    if ip is None:
+        ip = 0
+    if mask is True:
+        if ip != 0:
+            ip = int(ipaddress.IPv4Address(ip)) & 0xFFFFFFFF
+
+    return int(ipaddress.IPv4Address(ip))
+
 
 class ovs_dp_msg(genlmsg):
     # include the OVS version
@@ -49,6 +85,847 @@ class ovs_dp_msg(genlmsg):
     fields = genlmsg.fields + (("dpifindex", "I"),)
 
 
+class ovsactions(nla):
+    nla_flags = NLA_F_NESTED
+
+    nla_map = (
+        ("OVS_ACTION_ATTR_UNSPEC", "none"),
+        ("OVS_ACTION_ATTR_OUTPUT", "uint32"),
+        ("OVS_ACTION_ATTR_USERSPACE", "userspace"),
+        ("OVS_ACTION_ATTR_SET", "none"),
+        ("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
+        ("OVS_ACTION_ATTR_POP_VLAN", "flag"),
+        ("OVS_ACTION_ATTR_SAMPLE", "none"),
+        ("OVS_ACTION_ATTR_RECIRC", "uint32"),
+        ("OVS_ACTION_ATTR_HASH", "none"),
+        ("OVS_ACTION_ATTR_PUSH_MPLS", "none"),
+        ("OVS_ACTION_ATTR_POP_MPLS", "flag"),
+        ("OVS_ACTION_ATTR_SET_MASKED", "none"),
+        ("OVS_ACTION_ATTR_CT", "ctact"),
+        ("OVS_ACTION_ATTR_TRUNC", "uint32"),
+        ("OVS_ACTION_ATTR_PUSH_ETH", "none"),
+        ("OVS_ACTION_ATTR_POP_ETH", "flag"),
+        ("OVS_ACTION_ATTR_CT_CLEAR", "flag"),
+        ("OVS_ACTION_ATTR_PUSH_NSH", "none"),
+        ("OVS_ACTION_ATTR_POP_NSH", "flag"),
+        ("OVS_ACTION_ATTR_METER", "none"),
+        ("OVS_ACTION_ATTR_CLONE", "none"),
+        ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"),
+        ("OVS_ACTION_ATTR_ADD_MPLS", "none"),
+        ("OVS_ACTION_ATTR_DEC_TTL", "none"),
+    )
+
+    class ctact(nla):
+        nla_flags = NLA_F_NESTED
+
+        nla_map = (
+            ("OVS_CT_ATTR_NONE", "none"),
+            ("OVS_CT_ATTR_COMMIT", "flag"),
+            ("OVS_CT_ATTR_ZONE", "uint16"),
+            ("OVS_CT_ATTR_MARK", "none"),
+            ("OVS_CT_ATTR_LABELS", "none"),
+            ("OVS_CT_ATTR_HELPER", "asciiz"),
+            ("OVS_CT_ATTR_NAT", "natattr"),
+            ("OVS_CT_ATTR_FORCE_COMMIT", "flag"),
+            ("OVS_CT_ATTR_EVENTMASK", "uint32"),
+            ("OVS_CT_ATTR_TIMEOUT", "asciiz"),
+        )
+
+        class natattr(nla):
+            nla_flags = NLA_F_NESTED
+
+            nla_map = (
+                ("OVS_NAT_ATTR_NONE", "none"),
+                ("OVS_NAT_ATTR_SRC", "flag"),
+                ("OVS_NAT_ATTR_DST", "flag"),
+                ("OVS_NAT_ATTR_IP_MIN", "ipaddr"),
+                ("OVS_NAT_ATTR_IP_MAX", "ipaddr"),
+                ("OVS_NAT_ATTR_PROTO_MIN", "uint16"),
+                ("OVS_NAT_ATTR_PROTO_MAX", "uint16"),
+                ("OVS_NAT_ATTR_PERSISTENT", "flag"),
+                ("OVS_NAT_ATTR_PROTO_HASH", "flag"),
+                ("OVS_NAT_ATTR_PROTO_RANDOM", "flag"),
+            )
+
+            def dpstr(self, more=False):
+                print_str = "nat("
+
+                if self.get_attr("OVS_NAT_ATTR_SRC"):
+                    print_str += "src"
+                elif self.get_attr("OVS_NAT_ATTR_DST"):
+                    print_str += "dst"
+                else:
+                    print_str += "XXX-unknown-nat"
+
+                if self.get_attr("OVS_NAT_ATTR_IP_MIN") or self.get_attr(
+                    "OVS_NAT_ATTR_IP_MAX"
+                ):
+                    if self.get_attr("OVS_NAT_ATTR_IP_MIN"):
+                        print_str += "=%s," % str(
+                            self.get_attr("OVS_NAT_ATTR_IP_MIN")
+                        )
+
+                    if self.get_attr("OVS_NAT_ATTR_IP_MAX"):
+                        print_str += "-%s," % str(
+                            self.get_attr("OVS_NAT_ATTR_IP_MAX")
+                        )
+                else:
+                    print_str += ","
+
+                if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"):
+                    print_str += "proto_min=%d," % self.get_attr(
+                        "OVS_NAT_ATTR_PROTO_MIN"
+                    )
+
+                if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"):
+                    print_str += "proto_max=%d," % self.get_attr(
+                        "OVS_NAT_ATTR_PROTO_MAX"
+                    )
+
+                if self.get_attr("OVS_NAT_ATTR_PERSISTENT"):
+                    print_str += "persistent,"
+                if self.get_attr("OVS_NAT_ATTR_HASH"):
+                    print_str += "hash,"
+                if self.get_attr("OVS_NAT_ATTR_RANDOM"):
+                    print_str += "random"
+                print_str += ")"
+                return print_str
+
+        def dpstr(self, more=False):
+            print_str = "ct("
+
+            if self.get_attr("OVS_CT_ATTR_COMMIT") is not None:
+                print_str += "commit,"
+            if self.get_attr("OVS_CT_ATTR_ZONE") is not None:
+                print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE")
+            if self.get_attr("OVS_CT_ATTR_HELPER") is not None:
+                print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER")
+            if self.get_attr("OVS_CT_ATTR_NAT") is not None:
+                print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more)
+                print_str += ","
+            if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None:
+                print_str += "force,"
+            if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None:
+                print_str += "emask=0x%X," % self.get_attr(
+                    "OVS_CT_ATTR_EVENTMASK"
+                )
+            if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None:
+                print_str += "timeout=%s" % self.get_attr(
+                    "OVS_CT_ATTR_TIMEOUT"
+                )
+            print_str += ")"
+            return print_str
+
+    class userspace(nla):
+        nla_flags = NLA_F_NESTED
+
+        nla_map = (
+            ("OVS_USERSPACE_ATTR_UNUSED", "none"),
+            ("OVS_USERSPACE_ATTR_PID", "uint32"),
+            ("OVS_USERSPACE_ATTR_USERDATA", "array(uint8)"),
+            ("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", "uint32"),
+        )
+
+        def dpstr(self, more=False):
+            print_str = "userspace("
+            if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None:
+                print_str += "pid=%d," % self.get_attr(
+                    "OVS_USERSPACE_ATTR_PID"
+                )
+            if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None:
+                print_str += "userdata="
+                for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"):
+                    print_str += "%x." % f
+            if self.get_attr("OVS_USERSPACE_ATTR_TUN_PORT") is not None:
+                print_str += "egress_tun_port=%d" % self.get_attr(
+                    "OVS_USERSPACE_ATTR_TUN_PORT"
+                )
+            print_str += ")"
+            return print_str
+
+    def dpstr(self, more=False):
+        print_str = ""
+
+        for field in self.nla_map:
+            if field[1] == "none" or self.get_attr(field[0]) is None:
+                continue
+            if print_str != "":
+                print_str += ","
+
+            if field[1] == "uint32":
+                if field[0] == "OVS_ACTION_ATTR_OUTPUT":
+                    print_str += "%d" % int(self.get_attr(field[0]))
+                elif field[0] == "OVS_ACTION_ATTR_RECIRC":
+                    print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))
+                elif field[0] == "OVS_ACTION_ATTR_TRUNC":
+                    print_str += "trunc(%d)" % int(self.get_attr(field[0]))
+            elif field[1] == "flag":
+                if field[0] == "OVS_ACTION_ATTR_CT_CLEAR":
+                    print_str += "ct_clear"
+                elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":
+                    print_str += "pop_vlan"
+                elif field[0] == "OVS_ACTION_ATTR_POP_ETH":
+                    print_str += "pop_eth"
+                elif field[0] == "OVS_ACTION_ATTR_POP_NSH":
+                    print_str += "pop_nsh"
+                elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":
+                    print_str += "pop_mpls"
+            else:
+                datum = self.get_attr(field[0])
+                print_str += datum.dpstr(more)
+
+        return print_str
+
+
+class ovskey(nla):
+    nla_flags = NLA_F_NESTED
+    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", "be16"),
+        ("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", lambda x: int(x) if x is not None else 0),
+            ("dst", "dst", "%d", lambda x: int(x) if x is not None else 0),
+        )
+
+        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"),
+                convert_mac,
+            ),
+            (
+                "dst",
+                "dst",
+                macstr,
+                lambda x: int.from_bytes(x, "big"),
+                convert_mac,
+            ),
+        )
+
+        def __init__(
+            self,
+            data=None,
+            offset=None,
+            parent=None,
+            length=None,
+            init=None,
+        ):
+            ovskey.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,
+                convert_ipv4,
+            ),
+            (
+                "dst",
+                "dst",
+                lambda x: str(ipaddress.IPv4Address(x)),
+                int,
+                convert_ipv4,
+            ),
+            ("proto", "proto", "%d", lambda x: int(x) if x is not None else 0),
+            ("tos", "tos", "%d", lambda x: int(x) if x is not None else 0),
+            ("ttl", "ttl", "%d", lambda x: int(x) if x is not None else 0),
+            ("frag", "frag", "%d", lambda x: int(x) if x is not None else 0),
+        )
+
+        def __init__(
+            self,
+            data=None,
+            offset=None,
+            parent=None,
+            length=None,
+            init=None,
+        ):
+            ovskey.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"),
+                lambda x: ipaddress.IPv6Address(x),
+            ),
+            (
+                "dst",
+                "dst",
+                lambda x: str(ipaddress.IPv6Address(x)),
+                lambda x: int.from_bytes(x, "big"),
+                lambda x: ipaddress.IPv6Address(x),
+            ),
+            ("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,
+        ):
+            ovskey.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,
+        ):
+            ovskey.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,
+        ):
+            ovskey.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,
+        ):
+            ovskey.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,
+        ):
+            ovskey.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,
+        ):
+            ovskey.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,
+                convert_ipv4,
+            ),
+            (
+                "tip",
+                "tip",
+                lambda x: str(ipaddress.IPv4Address(x)),
+                int,
+                convert_ipv4,
+            ),
+            ("op", "op", "%d", lambda x: int(x) if x is not None else 0),
+            (
+                "sha",
+                "sha",
+                macstr,
+                lambda x: int.from_bytes(x, "big"),
+                convert_mac,
+            ),
+            (
+                "tha",
+                "tha",
+                macstr,
+                lambda x: int.from_bytes(x, "big"),
+                convert_mac,
+            ),
+        )
+
+        def __init__(
+            self,
+            data=None,
+            offset=None,
+            parent=None,
+            length=None,
+            init=None,
+        ):
+            ovskey.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,
+        ):
+            ovskey.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,
+        ):
+            ovskey.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", convertmac),
+            ),
+            (
+                "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,
+        ):
+            ovskey.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
+
+
 class OvsDatapath(GenericNetlinkSocket):
     OVS_DP_F_VPORT_PIDS = 1 << 1
     OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
@@ -300,6 +1177,135 @@ class OvsVport(GenericNetlinkSocket):
         return reply
 
 
+class OvsFlow(GenericNetlinkSocket):
+    class ovs_flow_msg(ovs_dp_msg):
+        nla_map = (
+            ("OVS_FLOW_ATTR_UNSPEC", "none"),
+            ("OVS_FLOW_ATTR_KEY", "ovskey"),
+            ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"),
+            ("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", "ovskey"),
+            ("OVS_FLOW_ATTR_PROBE", "none"),
+            ("OVS_FLOW_ATTR_UFID", "array(uint32)"),
+            ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"),
+        )
+
+        class flowstats(nla):
+            fields = (
+                ("packets", "=Q"),
+                ("bytes", "=Q"),
+            )
+
+        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 = key_field
+
+            mask_field = self.get_attr("OVS_FLOW_ATTR_MASK")
+            maskmsg = None
+            if mask_field is not None:
+                maskmsg = mask_field
+
+            acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS")
+            actsmsg = None
+            if acts_field is not None:
+                actsmsg = acts_field
+
+            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) - used_time
+                print_str += "{}s,".format(used_time / 1000)
+
+            print_str += " actions:"
+            if (
+                actsmsg is None
+                or "attrs" not in actsmsg
+                or len(actsmsg["attrs"]) == 0
+            ):
+                print_str += "drop"
+            else:
+                print_str += actsmsg.dpstr(more)
+
+            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")
@@ -340,12 +1346,16 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
 
 
 def main(argv):
+    nlmsg_atoms.ovskey = ovskey
+    nlmsg_atoms.ovsactions = ovsactions
+
     parser = argparse.ArgumentParser()
     parser.add_argument(
         "-v",
         "--verbose",
         action="count",
         help="Increment 'verbose' output counter.",
+        default=0,
     )
     subparsers = parser.add_subparsers()
 
@@ -387,10 +1397,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"):
@@ -441,6 +1459,14 @@ def main(argv):
             msg += " removed."
         else:
             msg += " failed to remove."
+    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
 
-- 
2.39.2


  parent reply	other threads:[~2023-04-14 13:18 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-14 13:17 [PATCH net-next 0/3] selftests: openvswitch: add support for testing upcall interface Aaron Conole
2023-04-14 13:17 ` [PATCH net-next 1/3] selftests: openvswitch: add interface support Aaron Conole
2023-04-14 13:17 ` Aaron Conole [this message]
2023-04-14 13:17 ` [PATCH net-next 3/3] selftests: openvswitch: add support for upcall testing Aaron Conole
2023-04-17  7:20 ` [PATCH net-next 0/3] selftests: openvswitch: add support for testing upcall interface patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230414131750.4185160-3-aconole@redhat.com \
    --to=aconole@redhat.com \
    --cc=davem@davemloft.net \
    --cc=dev@openvswitch.org \
    --cc=edumazet@google.com \
    --cc=i.maximets@ovn.org \
    --cc=kuba@kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=pshelar@ovn.org \
    --cc=shuah@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.