1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3
4# Controls the openvswitch module.  Part of the kselftest suite, but
5# can be used for some diagnostic purpose as well.
6
7import argparse
8import errno
9import ipaddress
10import logging
11import multiprocessing
12import struct
13import sys
14import time
15
16try:
17    from pyroute2 import NDB
18
19    from pyroute2.netlink import NLA_F_NESTED
20    from pyroute2.netlink import NLM_F_ACK
21    from pyroute2.netlink import NLM_F_DUMP
22    from pyroute2.netlink import NLM_F_REQUEST
23    from pyroute2.netlink import genlmsg
24    from pyroute2.netlink import nla
25    from pyroute2.netlink import nlmsg_atoms
26    from pyroute2.netlink.exceptions import NetlinkError
27    from pyroute2.netlink.generic import GenericNetlinkSocket
28except ModuleNotFoundError:
29    print("Need to install the python pyroute2 package.")
30    sys.exit(0)
31
32
33OVS_DATAPATH_FAMILY = "ovs_datapath"
34OVS_VPORT_FAMILY = "ovs_vport"
35OVS_FLOW_FAMILY = "ovs_flow"
36OVS_PACKET_FAMILY = "ovs_packet"
37OVS_METER_FAMILY = "ovs_meter"
38OVS_CT_LIMIT_FAMILY = "ovs_ct_limit"
39
40OVS_DATAPATH_VERSION = 2
41OVS_DP_CMD_NEW = 1
42OVS_DP_CMD_DEL = 2
43OVS_DP_CMD_GET = 3
44OVS_DP_CMD_SET = 4
45
46OVS_VPORT_CMD_NEW = 1
47OVS_VPORT_CMD_DEL = 2
48OVS_VPORT_CMD_GET = 3
49OVS_VPORT_CMD_SET = 4
50
51OVS_FLOW_CMD_NEW = 1
52OVS_FLOW_CMD_DEL = 2
53OVS_FLOW_CMD_GET = 3
54OVS_FLOW_CMD_SET = 4
55
56
57def macstr(mac):
58    outstr = ":".join(["%02X" % i for i in mac])
59    return outstr
60
61
62def convert_mac(mac_str, mask=False):
63    if mac_str is None or mac_str == "":
64        mac_str = "00:00:00:00:00:00"
65    if mask is True and mac_str != "00:00:00:00:00:00":
66        mac_str = "FF:FF:FF:FF:FF:FF"
67    mac_split = mac_str.split(":")
68    ret = bytearray([int(i, 16) for i in mac_split])
69    return bytes(ret)
70
71
72def convert_ipv4(ip, mask=False):
73    if ip is None:
74        ip = 0
75    if mask is True:
76        if ip != 0:
77            ip = int(ipaddress.IPv4Address(ip)) & 0xFFFFFFFF
78
79    return int(ipaddress.IPv4Address(ip))
80
81
82class ovs_dp_msg(genlmsg):
83    # include the OVS version
84    # We need a custom header rather than just being able to rely on
85    # genlmsg because fields ends up not expressing everything correctly
86    # if we use the canonical example of setting fields = (('customfield',),)
87    fields = genlmsg.fields + (("dpifindex", "I"),)
88
89
90class ovsactions(nla):
91    nla_flags = NLA_F_NESTED
92
93    nla_map = (
94        ("OVS_ACTION_ATTR_UNSPEC", "none"),
95        ("OVS_ACTION_ATTR_OUTPUT", "uint32"),
96        ("OVS_ACTION_ATTR_USERSPACE", "userspace"),
97        ("OVS_ACTION_ATTR_SET", "none"),
98        ("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
99        ("OVS_ACTION_ATTR_POP_VLAN", "flag"),
100        ("OVS_ACTION_ATTR_SAMPLE", "none"),
101        ("OVS_ACTION_ATTR_RECIRC", "uint32"),
102        ("OVS_ACTION_ATTR_HASH", "none"),
103        ("OVS_ACTION_ATTR_PUSH_MPLS", "none"),
104        ("OVS_ACTION_ATTR_POP_MPLS", "flag"),
105        ("OVS_ACTION_ATTR_SET_MASKED", "none"),
106        ("OVS_ACTION_ATTR_CT", "ctact"),
107        ("OVS_ACTION_ATTR_TRUNC", "uint32"),
108        ("OVS_ACTION_ATTR_PUSH_ETH", "none"),
109        ("OVS_ACTION_ATTR_POP_ETH", "flag"),
110        ("OVS_ACTION_ATTR_CT_CLEAR", "flag"),
111        ("OVS_ACTION_ATTR_PUSH_NSH", "none"),
112        ("OVS_ACTION_ATTR_POP_NSH", "flag"),
113        ("OVS_ACTION_ATTR_METER", "none"),
114        ("OVS_ACTION_ATTR_CLONE", "none"),
115        ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"),
116        ("OVS_ACTION_ATTR_ADD_MPLS", "none"),
117        ("OVS_ACTION_ATTR_DEC_TTL", "none"),
118    )
119
120    class ctact(nla):
121        nla_flags = NLA_F_NESTED
122
123        nla_map = (
124            ("OVS_CT_ATTR_NONE", "none"),
125            ("OVS_CT_ATTR_COMMIT", "flag"),
126            ("OVS_CT_ATTR_ZONE", "uint16"),
127            ("OVS_CT_ATTR_MARK", "none"),
128            ("OVS_CT_ATTR_LABELS", "none"),
129            ("OVS_CT_ATTR_HELPER", "asciiz"),
130            ("OVS_CT_ATTR_NAT", "natattr"),
131            ("OVS_CT_ATTR_FORCE_COMMIT", "flag"),
132            ("OVS_CT_ATTR_EVENTMASK", "uint32"),
133            ("OVS_CT_ATTR_TIMEOUT", "asciiz"),
134        )
135
136        class natattr(nla):
137            nla_flags = NLA_F_NESTED
138
139            nla_map = (
140                ("OVS_NAT_ATTR_NONE", "none"),
141                ("OVS_NAT_ATTR_SRC", "flag"),
142                ("OVS_NAT_ATTR_DST", "flag"),
143                ("OVS_NAT_ATTR_IP_MIN", "ipaddr"),
144                ("OVS_NAT_ATTR_IP_MAX", "ipaddr"),
145                ("OVS_NAT_ATTR_PROTO_MIN", "uint16"),
146                ("OVS_NAT_ATTR_PROTO_MAX", "uint16"),
147                ("OVS_NAT_ATTR_PERSISTENT", "flag"),
148                ("OVS_NAT_ATTR_PROTO_HASH", "flag"),
149                ("OVS_NAT_ATTR_PROTO_RANDOM", "flag"),
150            )
151
152            def dpstr(self, more=False):
153                print_str = "nat("
154
155                if self.get_attr("OVS_NAT_ATTR_SRC"):
156                    print_str += "src"
157                elif self.get_attr("OVS_NAT_ATTR_DST"):
158                    print_str += "dst"
159                else:
160                    print_str += "XXX-unknown-nat"
161
162                if self.get_attr("OVS_NAT_ATTR_IP_MIN") or self.get_attr(
163                    "OVS_NAT_ATTR_IP_MAX"
164                ):
165                    if self.get_attr("OVS_NAT_ATTR_IP_MIN"):
166                        print_str += "=%s," % str(
167                            self.get_attr("OVS_NAT_ATTR_IP_MIN")
168                        )
169
170                    if self.get_attr("OVS_NAT_ATTR_IP_MAX"):
171                        print_str += "-%s," % str(
172                            self.get_attr("OVS_NAT_ATTR_IP_MAX")
173                        )
174                else:
175                    print_str += ","
176
177                if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"):
178                    print_str += "proto_min=%d," % self.get_attr(
179                        "OVS_NAT_ATTR_PROTO_MIN"
180                    )
181
182                if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"):
183                    print_str += "proto_max=%d," % self.get_attr(
184                        "OVS_NAT_ATTR_PROTO_MAX"
185                    )
186
187                if self.get_attr("OVS_NAT_ATTR_PERSISTENT"):
188                    print_str += "persistent,"
189                if self.get_attr("OVS_NAT_ATTR_HASH"):
190                    print_str += "hash,"
191                if self.get_attr("OVS_NAT_ATTR_RANDOM"):
192                    print_str += "random"
193                print_str += ")"
194                return print_str
195
196        def dpstr(self, more=False):
197            print_str = "ct("
198
199            if self.get_attr("OVS_CT_ATTR_COMMIT") is not None:
200                print_str += "commit,"
201            if self.get_attr("OVS_CT_ATTR_ZONE") is not None:
202                print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE")
203            if self.get_attr("OVS_CT_ATTR_HELPER") is not None:
204                print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER")
205            if self.get_attr("OVS_CT_ATTR_NAT") is not None:
206                print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more)
207                print_str += ","
208            if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None:
209                print_str += "force,"
210            if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None:
211                print_str += "emask=0x%X," % self.get_attr(
212                    "OVS_CT_ATTR_EVENTMASK"
213                )
214            if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None:
215                print_str += "timeout=%s" % self.get_attr(
216                    "OVS_CT_ATTR_TIMEOUT"
217                )
218            print_str += ")"
219            return print_str
220
221    class userspace(nla):
222        nla_flags = NLA_F_NESTED
223
224        nla_map = (
225            ("OVS_USERSPACE_ATTR_UNUSED", "none"),
226            ("OVS_USERSPACE_ATTR_PID", "uint32"),
227            ("OVS_USERSPACE_ATTR_USERDATA", "array(uint8)"),
228            ("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", "uint32"),
229        )
230
231        def dpstr(self, more=False):
232            print_str = "userspace("
233            if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None:
234                print_str += "pid=%d," % self.get_attr(
235                    "OVS_USERSPACE_ATTR_PID"
236                )
237            if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None:
238                print_str += "userdata="
239                for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"):
240                    print_str += "%x." % f
241            if self.get_attr("OVS_USERSPACE_ATTR_TUN_PORT") is not None:
242                print_str += "egress_tun_port=%d" % self.get_attr(
243                    "OVS_USERSPACE_ATTR_TUN_PORT"
244                )
245            print_str += ")"
246            return print_str
247
248    def dpstr(self, more=False):
249        print_str = ""
250
251        for field in self.nla_map:
252            if field[1] == "none" or self.get_attr(field[0]) is None:
253                continue
254            if print_str != "":
255                print_str += ","
256
257            if field[1] == "uint32":
258                if field[0] == "OVS_ACTION_ATTR_OUTPUT":
259                    print_str += "%d" % int(self.get_attr(field[0]))
260                elif field[0] == "OVS_ACTION_ATTR_RECIRC":
261                    print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))
262                elif field[0] == "OVS_ACTION_ATTR_TRUNC":
263                    print_str += "trunc(%d)" % int(self.get_attr(field[0]))
264            elif field[1] == "flag":
265                if field[0] == "OVS_ACTION_ATTR_CT_CLEAR":
266                    print_str += "ct_clear"
267                elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":
268                    print_str += "pop_vlan"
269                elif field[0] == "OVS_ACTION_ATTR_POP_ETH":
270                    print_str += "pop_eth"
271                elif field[0] == "OVS_ACTION_ATTR_POP_NSH":
272                    print_str += "pop_nsh"
273                elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":
274                    print_str += "pop_mpls"
275            else:
276                datum = self.get_attr(field[0])
277                print_str += datum.dpstr(more)
278
279        return print_str
280
281
282class ovskey(nla):
283    nla_flags = NLA_F_NESTED
284    nla_map = (
285        ("OVS_KEY_ATTR_UNSPEC", "none"),
286        ("OVS_KEY_ATTR_ENCAP", "none"),
287        ("OVS_KEY_ATTR_PRIORITY", "uint32"),
288        ("OVS_KEY_ATTR_IN_PORT", "uint32"),
289        ("OVS_KEY_ATTR_ETHERNET", "ethaddr"),
290        ("OVS_KEY_ATTR_VLAN", "uint16"),
291        ("OVS_KEY_ATTR_ETHERTYPE", "be16"),
292        ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"),
293        ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"),
294        ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"),
295        ("OVS_KEY_ATTR_UDP", "ovs_key_udp"),
296        ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"),
297        ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"),
298        ("OVS_KEY_ATTR_ARP", "ovs_key_arp"),
299        ("OVS_KEY_ATTR_ND", "ovs_key_nd"),
300        ("OVS_KEY_ATTR_SKB_MARK", "uint32"),
301        ("OVS_KEY_ATTR_TUNNEL", "none"),
302        ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"),
303        ("OVS_KEY_ATTR_TCP_FLAGS", "be16"),
304        ("OVS_KEY_ATTR_DP_HASH", "uint32"),
305        ("OVS_KEY_ATTR_RECIRC_ID", "uint32"),
306        ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"),
307        ("OVS_KEY_ATTR_CT_STATE", "uint32"),
308        ("OVS_KEY_ATTR_CT_ZONE", "uint16"),
309        ("OVS_KEY_ATTR_CT_MARK", "uint32"),
310        ("OVS_KEY_ATTR_CT_LABELS", "none"),
311        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"),
312        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"),
313        ("OVS_KEY_ATTR_NSH", "none"),
314        ("OVS_KEY_ATTR_PACKET_TYPE", "none"),
315        ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"),
316        ("OVS_KEY_ATTR_TUNNEL_INFO", "none"),
317        ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"),
318    )
319
320    class ovs_key_proto(nla):
321        fields = (
322            ("src", "!H"),
323            ("dst", "!H"),
324        )
325
326        fields_map = (
327            ("src", "src", "%d", lambda x: int(x) if x is not None else 0),
328            ("dst", "dst", "%d", lambda x: int(x) if x is not None else 0),
329        )
330
331        def __init__(
332            self,
333            protostr,
334            data=None,
335            offset=None,
336            parent=None,
337            length=None,
338            init=None,
339        ):
340            self.proto_str = protostr
341            nla.__init__(
342                self,
343                data=data,
344                offset=offset,
345                parent=parent,
346                length=length,
347                init=init,
348            )
349
350        def dpstr(self, masked=None, more=False):
351            outstr = self.proto_str + "("
352            first = False
353            for f in self.fields_map:
354                if first:
355                    outstr += ","
356                if masked is None:
357                    outstr += "%s=" % f[0]
358                    if isinstance(f[2], str):
359                        outstr += f[2] % self[f[1]]
360                    else:
361                        outstr += f[2](self[f[1]])
362                    first = True
363                elif more or f[3](masked[f[1]]) != 0:
364                    outstr += "%s=" % f[0]
365                    if isinstance(f[2], str):
366                        outstr += f[2] % self[f[1]]
367                    else:
368                        outstr += f[2](self[f[1]])
369                    outstr += "/"
370                    if isinstance(f[2], str):
371                        outstr += f[2] % masked[f[1]]
372                    else:
373                        outstr += f[2](masked[f[1]])
374                    first = True
375            outstr += ")"
376            return outstr
377
378    class ethaddr(ovs_key_proto):
379        fields = (
380            ("src", "!6s"),
381            ("dst", "!6s"),
382        )
383
384        fields_map = (
385            (
386                "src",
387                "src",
388                macstr,
389                lambda x: int.from_bytes(x, "big"),
390                convert_mac,
391            ),
392            (
393                "dst",
394                "dst",
395                macstr,
396                lambda x: int.from_bytes(x, "big"),
397                convert_mac,
398            ),
399        )
400
401        def __init__(
402            self,
403            data=None,
404            offset=None,
405            parent=None,
406            length=None,
407            init=None,
408        ):
409            ovskey.ovs_key_proto.__init__(
410                self,
411                "eth",
412                data=data,
413                offset=offset,
414                parent=parent,
415                length=length,
416                init=init,
417            )
418
419    class ovs_key_ipv4(ovs_key_proto):
420        fields = (
421            ("src", "!I"),
422            ("dst", "!I"),
423            ("proto", "B"),
424            ("tos", "B"),
425            ("ttl", "B"),
426            ("frag", "B"),
427        )
428
429        fields_map = (
430            (
431                "src",
432                "src",
433                lambda x: str(ipaddress.IPv4Address(x)),
434                int,
435                convert_ipv4,
436            ),
437            (
438                "dst",
439                "dst",
440                lambda x: str(ipaddress.IPv4Address(x)),
441                int,
442                convert_ipv4,
443            ),
444            ("proto", "proto", "%d", lambda x: int(x) if x is not None else 0),
445            ("tos", "tos", "%d", lambda x: int(x) if x is not None else 0),
446            ("ttl", "ttl", "%d", lambda x: int(x) if x is not None else 0),
447            ("frag", "frag", "%d", lambda x: int(x) if x is not None else 0),
448        )
449
450        def __init__(
451            self,
452            data=None,
453            offset=None,
454            parent=None,
455            length=None,
456            init=None,
457        ):
458            ovskey.ovs_key_proto.__init__(
459                self,
460                "ipv4",
461                data=data,
462                offset=offset,
463                parent=parent,
464                length=length,
465                init=init,
466            )
467
468    class ovs_key_ipv6(ovs_key_proto):
469        fields = (
470            ("src", "!16s"),
471            ("dst", "!16s"),
472            ("label", "!I"),
473            ("proto", "B"),
474            ("tclass", "B"),
475            ("hlimit", "B"),
476            ("frag", "B"),
477        )
478
479        fields_map = (
480            (
481                "src",
482                "src",
483                lambda x: str(ipaddress.IPv6Address(x)),
484                lambda x: int.from_bytes(x, "big"),
485                lambda x: ipaddress.IPv6Address(x),
486            ),
487            (
488                "dst",
489                "dst",
490                lambda x: str(ipaddress.IPv6Address(x)),
491                lambda x: int.from_bytes(x, "big"),
492                lambda x: ipaddress.IPv6Address(x),
493            ),
494            ("label", "label", "%d", int),
495            ("proto", "proto", "%d", int),
496            ("tclass", "tclass", "%d", int),
497            ("hlimit", "hlimit", "%d", int),
498            ("frag", "frag", "%d", int),
499        )
500
501        def __init__(
502            self,
503            data=None,
504            offset=None,
505            parent=None,
506            length=None,
507            init=None,
508        ):
509            ovskey.ovs_key_proto.__init__(
510                self,
511                "ipv6",
512                data=data,
513                offset=offset,
514                parent=parent,
515                length=length,
516                init=init,
517            )
518
519    class ovs_key_tcp(ovs_key_proto):
520        def __init__(
521            self,
522            data=None,
523            offset=None,
524            parent=None,
525            length=None,
526            init=None,
527        ):
528            ovskey.ovs_key_proto.__init__(
529                self,
530                "tcp",
531                data=data,
532                offset=offset,
533                parent=parent,
534                length=length,
535                init=init,
536            )
537
538    class ovs_key_udp(ovs_key_proto):
539        def __init__(
540            self,
541            data=None,
542            offset=None,
543            parent=None,
544            length=None,
545            init=None,
546        ):
547            ovskey.ovs_key_proto.__init__(
548                self,
549                "udp",
550                data=data,
551                offset=offset,
552                parent=parent,
553                length=length,
554                init=init,
555            )
556
557    class ovs_key_sctp(ovs_key_proto):
558        def __init__(
559            self,
560            data=None,
561            offset=None,
562            parent=None,
563            length=None,
564            init=None,
565        ):
566            ovskey.ovs_key_proto.__init__(
567                self,
568                "sctp",
569                data=data,
570                offset=offset,
571                parent=parent,
572                length=length,
573                init=init,
574            )
575
576    class ovs_key_icmp(ovs_key_proto):
577        fields = (
578            ("type", "B"),
579            ("code", "B"),
580        )
581
582        fields_map = (
583            ("type", "type", "%d", int),
584            ("code", "code", "%d", int),
585        )
586
587        def __init__(
588            self,
589            data=None,
590            offset=None,
591            parent=None,
592            length=None,
593            init=None,
594        ):
595            ovskey.ovs_key_proto.__init__(
596                self,
597                "icmp",
598                data=data,
599                offset=offset,
600                parent=parent,
601                length=length,
602                init=init,
603            )
604
605    class ovs_key_icmpv6(ovs_key_icmp):
606        def __init__(
607            self,
608            data=None,
609            offset=None,
610            parent=None,
611            length=None,
612            init=None,
613        ):
614            ovskey.ovs_key_proto.__init__(
615                self,
616                "icmpv6",
617                data=data,
618                offset=offset,
619                parent=parent,
620                length=length,
621                init=init,
622            )
623
624    class ovs_key_arp(ovs_key_proto):
625        fields = (
626            ("sip", "!I"),
627            ("tip", "!I"),
628            ("op", "!H"),
629            ("sha", "!6s"),
630            ("tha", "!6s"),
631            ("pad", "xx"),
632        )
633
634        fields_map = (
635            (
636                "sip",
637                "sip",
638                lambda x: str(ipaddress.IPv4Address(x)),
639                int,
640                convert_ipv4,
641            ),
642            (
643                "tip",
644                "tip",
645                lambda x: str(ipaddress.IPv4Address(x)),
646                int,
647                convert_ipv4,
648            ),
649            ("op", "op", "%d", lambda x: int(x) if x is not None else 0),
650            (
651                "sha",
652                "sha",
653                macstr,
654                lambda x: int.from_bytes(x, "big"),
655                convert_mac,
656            ),
657            (
658                "tha",
659                "tha",
660                macstr,
661                lambda x: int.from_bytes(x, "big"),
662                convert_mac,
663            ),
664        )
665
666        def __init__(
667            self,
668            data=None,
669            offset=None,
670            parent=None,
671            length=None,
672            init=None,
673        ):
674            ovskey.ovs_key_proto.__init__(
675                self,
676                "arp",
677                data=data,
678                offset=offset,
679                parent=parent,
680                length=length,
681                init=init,
682            )
683
684    class ovs_key_nd(ovs_key_proto):
685        fields = (
686            ("target", "!16s"),
687            ("sll", "!6s"),
688            ("tll", "!6s"),
689        )
690
691        fields_map = (
692            (
693                "target",
694                "target",
695                lambda x: str(ipaddress.IPv6Address(x)),
696                lambda x: int.from_bytes(x, "big"),
697            ),
698            ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")),
699            ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")),
700        )
701
702        def __init__(
703            self,
704            data=None,
705            offset=None,
706            parent=None,
707            length=None,
708            init=None,
709        ):
710            ovskey.ovs_key_proto.__init__(
711                self,
712                "nd",
713                data=data,
714                offset=offset,
715                parent=parent,
716                length=length,
717                init=init,
718            )
719
720    class ovs_key_ct_tuple_ipv4(ovs_key_proto):
721        fields = (
722            ("src", "!I"),
723            ("dst", "!I"),
724            ("tp_src", "!H"),
725            ("tp_dst", "!H"),
726            ("proto", "B"),
727        )
728
729        fields_map = (
730            (
731                "src",
732                "src",
733                lambda x: str(ipaddress.IPv4Address(x)),
734                int,
735            ),
736            (
737                "dst",
738                "dst",
739                lambda x: str(ipaddress.IPv6Address(x)),
740                int,
741            ),
742            ("tp_src", "tp_src", "%d", int),
743            ("tp_dst", "tp_dst", "%d", int),
744            ("proto", "proto", "%d", int),
745        )
746
747        def __init__(
748            self,
749            data=None,
750            offset=None,
751            parent=None,
752            length=None,
753            init=None,
754        ):
755            ovskey.ovs_key_proto.__init__(
756                self,
757                "ct_tuple4",
758                data=data,
759                offset=offset,
760                parent=parent,
761                length=length,
762                init=init,
763            )
764
765    class ovs_key_ct_tuple_ipv6(nla):
766        fields = (
767            ("src", "!16s"),
768            ("dst", "!16s"),
769            ("tp_src", "!H"),
770            ("tp_dst", "!H"),
771            ("proto", "B"),
772        )
773
774        fields_map = (
775            (
776                "src",
777                "src",
778                lambda x: str(ipaddress.IPv6Address(x)),
779                lambda x: int.from_bytes(x, "big", convertmac),
780            ),
781            (
782                "dst",
783                "dst",
784                lambda x: str(ipaddress.IPv6Address(x)),
785                lambda x: int.from_bytes(x, "big"),
786            ),
787            ("tp_src", "tp_src", "%d", int),
788            ("tp_dst", "tp_dst", "%d", int),
789            ("proto", "proto", "%d", int),
790        )
791
792        def __init__(
793            self,
794            data=None,
795            offset=None,
796            parent=None,
797            length=None,
798            init=None,
799        ):
800            ovskey.ovs_key_proto.__init__(
801                self,
802                "ct_tuple6",
803                data=data,
804                offset=offset,
805                parent=parent,
806                length=length,
807                init=init,
808            )
809
810    class ovs_key_mpls(nla):
811        fields = (("lse", ">I"),)
812
813    def dpstr(self, mask=None, more=False):
814        print_str = ""
815
816        for field in (
817            (
818                "OVS_KEY_ATTR_PRIORITY",
819                "skb_priority",
820                "%d",
821                lambda x: False,
822                True,
823            ),
824            (
825                "OVS_KEY_ATTR_SKB_MARK",
826                "skb_mark",
827                "%d",
828                lambda x: False,
829                True,
830            ),
831            (
832                "OVS_KEY_ATTR_RECIRC_ID",
833                "recirc_id",
834                "0x%08X",
835                lambda x: False,
836                True,
837            ),
838            (
839                "OVS_KEY_ATTR_DP_HASH",
840                "dp_hash",
841                "0x%08X",
842                lambda x: False,
843                True,
844            ),
845            (
846                "OVS_KEY_ATTR_CT_STATE",
847                "ct_state",
848                "0x%04x",
849                lambda x: False,
850                True,
851            ),
852            (
853                "OVS_KEY_ATTR_CT_ZONE",
854                "ct_zone",
855                "0x%04x",
856                lambda x: False,
857                True,
858            ),
859            (
860                "OVS_KEY_ATTR_CT_MARK",
861                "ct_mark",
862                "0x%08x",
863                lambda x: False,
864                True,
865            ),
866            (
867                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4",
868                None,
869                None,
870                False,
871                False,
872            ),
873            (
874                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6",
875                None,
876                None,
877                False,
878                False,
879            ),
880            (
881                "OVS_KEY_ATTR_IN_PORT",
882                "in_port",
883                "%d",
884                lambda x: True,
885                True,
886            ),
887            ("OVS_KEY_ATTR_ETHERNET", None, None, False, False),
888            (
889                "OVS_KEY_ATTR_ETHERTYPE",
890                "eth_type",
891                "0x%04x",
892                lambda x: int(x) == 0xFFFF,
893                True,
894            ),
895            ("OVS_KEY_ATTR_IPV4", None, None, False, False),
896            ("OVS_KEY_ATTR_IPV6", None, None, False, False),
897            ("OVS_KEY_ATTR_ARP", None, None, False, False),
898            ("OVS_KEY_ATTR_TCP", None, None, False, False),
899            (
900                "OVS_KEY_ATTR_TCP_FLAGS",
901                "tcp_flags",
902                "0x%04x",
903                lambda x: False,
904                True,
905            ),
906            ("OVS_KEY_ATTR_UDP", None, None, False, False),
907            ("OVS_KEY_ATTR_SCTP", None, None, False, False),
908            ("OVS_KEY_ATTR_ICMP", None, None, False, False),
909            ("OVS_KEY_ATTR_ICMPV6", None, None, False, False),
910            ("OVS_KEY_ATTR_ND", None, None, False, False),
911        ):
912            v = self.get_attr(field[0])
913            if v is not None:
914                m = None if mask is None else mask.get_attr(field[0])
915                if field[4] is False:
916                    print_str += v.dpstr(m, more)
917                    print_str += ","
918                else:
919                    if m is None or field[3](m):
920                        print_str += field[1] + "("
921                        print_str += field[2] % v
922                        print_str += "),"
923                    elif more or m != 0:
924                        print_str += field[1] + "("
925                        print_str += (field[2] % v) + "/" + (field[2] % m)
926                        print_str += "),"
927
928        return print_str
929
930
931class OvsPacket(GenericNetlinkSocket):
932    OVS_PACKET_CMD_MISS = 1  # Flow table miss
933    OVS_PACKET_CMD_ACTION = 2  # USERSPACE action
934    OVS_PACKET_CMD_EXECUTE = 3  # Apply actions to packet
935
936    class ovs_packet_msg(ovs_dp_msg):
937        nla_map = (
938            ("OVS_PACKET_ATTR_UNSPEC", "none"),
939            ("OVS_PACKET_ATTR_PACKET", "array(uint8)"),
940            ("OVS_PACKET_ATTR_KEY", "ovskey"),
941            ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"),
942            ("OVS_PACKET_ATTR_USERDATA", "none"),
943            ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"),
944            ("OVS_PACKET_ATTR_UNUSED1", "none"),
945            ("OVS_PACKET_ATTR_UNUSED2", "none"),
946            ("OVS_PACKET_ATTR_PROBE", "none"),
947            ("OVS_PACKET_ATTR_MRU", "uint16"),
948            ("OVS_PACKET_ATTR_LEN", "uint32"),
949            ("OVS_PACKET_ATTR_HASH", "uint64"),
950        )
951
952    def __init__(self):
953        GenericNetlinkSocket.__init__(self)
954        self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg)
955
956    def upcall_handler(self, up=None):
957        print("listening on upcall packet handler:", self.epid)
958        while True:
959            try:
960                msgs = self.get()
961                for msg in msgs:
962                    if not up:
963                        continue
964                    if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS:
965                        up.miss(msg)
966                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION:
967                        up.action(msg)
968                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE:
969                        up.execute(msg)
970                    else:
971                        print("Unkonwn cmd: %d" % msg["cmd"])
972            except NetlinkError as ne:
973                raise ne
974
975
976class OvsDatapath(GenericNetlinkSocket):
977    OVS_DP_F_VPORT_PIDS = 1 << 1
978    OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
979
980    class dp_cmd_msg(ovs_dp_msg):
981        """
982        Message class that will be used to communicate with the kernel module.
983        """
984
985        nla_map = (
986            ("OVS_DP_ATTR_UNSPEC", "none"),
987            ("OVS_DP_ATTR_NAME", "asciiz"),
988            ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"),
989            ("OVS_DP_ATTR_STATS", "dpstats"),
990            ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"),
991            ("OVS_DP_ATTR_USER_FEATURES", "uint32"),
992            ("OVS_DP_ATTR_PAD", "none"),
993            ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"),
994            ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"),
995        )
996
997        class dpstats(nla):
998            fields = (
999                ("hit", "=Q"),
1000                ("missed", "=Q"),
1001                ("lost", "=Q"),
1002                ("flows", "=Q"),
1003            )
1004
1005        class megaflowstats(nla):
1006            fields = (
1007                ("mask_hit", "=Q"),
1008                ("masks", "=I"),
1009                ("padding", "=I"),
1010                ("cache_hits", "=Q"),
1011                ("pad1", "=Q"),
1012            )
1013
1014    def __init__(self):
1015        GenericNetlinkSocket.__init__(self)
1016        self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg)
1017
1018    def info(self, dpname, ifindex=0):
1019        msg = OvsDatapath.dp_cmd_msg()
1020        msg["cmd"] = OVS_DP_CMD_GET
1021        msg["version"] = OVS_DATAPATH_VERSION
1022        msg["reserved"] = 0
1023        msg["dpifindex"] = ifindex
1024        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1025
1026        try:
1027            reply = self.nlm_request(
1028                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST
1029            )
1030            reply = reply[0]
1031        except NetlinkError as ne:
1032            if ne.code == errno.ENODEV:
1033                reply = None
1034            else:
1035                raise ne
1036
1037        return reply
1038
1039    def create(
1040        self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket()
1041    ):
1042        msg = OvsDatapath.dp_cmd_msg()
1043        msg["cmd"] = OVS_DP_CMD_NEW
1044        if versionStr is None:
1045            msg["version"] = OVS_DATAPATH_VERSION
1046        else:
1047            msg["version"] = int(versionStr.split(":")[0], 0)
1048        msg["reserved"] = 0
1049        msg["dpifindex"] = 0
1050        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1051
1052        dpfeatures = 0
1053        if versionStr is not None and versionStr.find(":") != -1:
1054            dpfeatures = int(versionStr.split(":")[1], 0)
1055        else:
1056            if versionStr is None or versionStr.find(":") == -1:
1057                dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU
1058                dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS
1059
1060            nproc = multiprocessing.cpu_count()
1061            procarray = []
1062            for i in range(1, nproc):
1063                procarray += [int(p.epid)]
1064            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray])
1065        msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures])
1066        if not shouldUpcall:
1067            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]])
1068
1069        try:
1070            reply = self.nlm_request(
1071                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1072            )
1073            reply = reply[0]
1074        except NetlinkError as ne:
1075            if ne.code == errno.EEXIST:
1076                reply = None
1077            else:
1078                raise ne
1079
1080        return reply
1081
1082    def destroy(self, dpname):
1083        msg = OvsDatapath.dp_cmd_msg()
1084        msg["cmd"] = OVS_DP_CMD_DEL
1085        msg["version"] = OVS_DATAPATH_VERSION
1086        msg["reserved"] = 0
1087        msg["dpifindex"] = 0
1088        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1089
1090        try:
1091            reply = self.nlm_request(
1092                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1093            )
1094            reply = reply[0]
1095        except NetlinkError as ne:
1096            if ne.code == errno.ENODEV:
1097                reply = None
1098            else:
1099                raise ne
1100
1101        return reply
1102
1103
1104class OvsVport(GenericNetlinkSocket):
1105    OVS_VPORT_TYPE_NETDEV = 1
1106    OVS_VPORT_TYPE_INTERNAL = 2
1107    OVS_VPORT_TYPE_GRE = 3
1108    OVS_VPORT_TYPE_VXLAN = 4
1109    OVS_VPORT_TYPE_GENEVE = 5
1110
1111    class ovs_vport_msg(ovs_dp_msg):
1112        nla_map = (
1113            ("OVS_VPORT_ATTR_UNSPEC", "none"),
1114            ("OVS_VPORT_ATTR_PORT_NO", "uint32"),
1115            ("OVS_VPORT_ATTR_TYPE", "uint32"),
1116            ("OVS_VPORT_ATTR_NAME", "asciiz"),
1117            ("OVS_VPORT_ATTR_OPTIONS", "none"),
1118            ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"),
1119            ("OVS_VPORT_ATTR_STATS", "vportstats"),
1120            ("OVS_VPORT_ATTR_PAD", "none"),
1121            ("OVS_VPORT_ATTR_IFINDEX", "uint32"),
1122            ("OVS_VPORT_ATTR_NETNSID", "uint32"),
1123        )
1124
1125        class vportstats(nla):
1126            fields = (
1127                ("rx_packets", "=Q"),
1128                ("tx_packets", "=Q"),
1129                ("rx_bytes", "=Q"),
1130                ("tx_bytes", "=Q"),
1131                ("rx_errors", "=Q"),
1132                ("tx_errors", "=Q"),
1133                ("rx_dropped", "=Q"),
1134                ("tx_dropped", "=Q"),
1135            )
1136
1137    def type_to_str(vport_type):
1138        if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:
1139            return "netdev"
1140        elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
1141            return "internal"
1142        elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE:
1143            return "gre"
1144        elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN:
1145            return "vxlan"
1146        elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE:
1147            return "geneve"
1148        raise ValueError("Unknown vport type:%d" % vport_type)
1149
1150    def str_to_type(vport_type):
1151        if vport_type == "netdev":
1152            return OvsVport.OVS_VPORT_TYPE_NETDEV
1153        elif vport_type == "internal":
1154            return OvsVport.OVS_VPORT_TYPE_INTERNAL
1155        elif vport_type == "gre":
1156            return OvsVport.OVS_VPORT_TYPE_INTERNAL
1157        elif vport_type == "vxlan":
1158            return OvsVport.OVS_VPORT_TYPE_VXLAN
1159        elif vport_type == "geneve":
1160            return OvsVport.OVS_VPORT_TYPE_GENEVE
1161        raise ValueError("Unknown vport type: '%s'" % vport_type)
1162
1163    def __init__(self, packet=OvsPacket()):
1164        GenericNetlinkSocket.__init__(self)
1165        self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg)
1166        self.upcall_packet = packet
1167
1168    def info(self, vport_name, dpifindex=0, portno=None):
1169        msg = OvsVport.ovs_vport_msg()
1170
1171        msg["cmd"] = OVS_VPORT_CMD_GET
1172        msg["version"] = OVS_DATAPATH_VERSION
1173        msg["reserved"] = 0
1174        msg["dpifindex"] = dpifindex
1175
1176        if portno is None:
1177            msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name])
1178        else:
1179            msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno])
1180
1181        try:
1182            reply = self.nlm_request(
1183                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST
1184            )
1185            reply = reply[0]
1186        except NetlinkError as ne:
1187            if ne.code == errno.ENODEV:
1188                reply = None
1189            else:
1190                raise ne
1191        return reply
1192
1193    def attach(self, dpindex, vport_ifname, ptype):
1194        msg = OvsVport.ovs_vport_msg()
1195
1196        msg["cmd"] = OVS_VPORT_CMD_NEW
1197        msg["version"] = OVS_DATAPATH_VERSION
1198        msg["reserved"] = 0
1199        msg["dpifindex"] = dpindex
1200        port_type = OvsVport.str_to_type(ptype)
1201
1202        msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type])
1203        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
1204        msg["attrs"].append(
1205            ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]]
1206        )
1207
1208        try:
1209            reply = self.nlm_request(
1210                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1211            )
1212            reply = reply[0]
1213        except NetlinkError as ne:
1214            if ne.code == errno.EEXIST:
1215                reply = None
1216            else:
1217                raise ne
1218        return reply
1219
1220    def reset_upcall(self, dpindex, vport_ifname, p=None):
1221        msg = OvsVport.ovs_vport_msg()
1222
1223        msg["cmd"] = OVS_VPORT_CMD_SET
1224        msg["version"] = OVS_DATAPATH_VERSION
1225        msg["reserved"] = 0
1226        msg["dpifindex"] = dpindex
1227        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
1228
1229        if p == None:
1230            p = self.upcall_packet
1231        else:
1232            self.upcall_packet = p
1233
1234        msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]])
1235
1236        try:
1237            reply = self.nlm_request(
1238                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1239            )
1240            reply = reply[0]
1241        except NetlinkError as ne:
1242            raise ne
1243        return reply
1244
1245    def detach(self, dpindex, vport_ifname):
1246        msg = OvsVport.ovs_vport_msg()
1247
1248        msg["cmd"] = OVS_VPORT_CMD_DEL
1249        msg["version"] = OVS_DATAPATH_VERSION
1250        msg["reserved"] = 0
1251        msg["dpifindex"] = dpindex
1252        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
1253
1254        try:
1255            reply = self.nlm_request(
1256                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1257            )
1258            reply = reply[0]
1259        except NetlinkError as ne:
1260            if ne.code == errno.ENODEV:
1261                reply = None
1262            else:
1263                raise ne
1264        return reply
1265
1266    def upcall_handler(self, handler=None):
1267        self.upcall_packet.upcall_handler(handler)
1268
1269
1270class OvsFlow(GenericNetlinkSocket):
1271    class ovs_flow_msg(ovs_dp_msg):
1272        nla_map = (
1273            ("OVS_FLOW_ATTR_UNSPEC", "none"),
1274            ("OVS_FLOW_ATTR_KEY", "ovskey"),
1275            ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"),
1276            ("OVS_FLOW_ATTR_STATS", "flowstats"),
1277            ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"),
1278            ("OVS_FLOW_ATTR_USED", "uint64"),
1279            ("OVS_FLOW_ATTR_CLEAR", "none"),
1280            ("OVS_FLOW_ATTR_MASK", "ovskey"),
1281            ("OVS_FLOW_ATTR_PROBE", "none"),
1282            ("OVS_FLOW_ATTR_UFID", "array(uint32)"),
1283            ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"),
1284        )
1285
1286        class flowstats(nla):
1287            fields = (
1288                ("packets", "=Q"),
1289                ("bytes", "=Q"),
1290            )
1291
1292        def dpstr(self, more=False):
1293            ufid = self.get_attr("OVS_FLOW_ATTR_UFID")
1294            ufid_str = ""
1295            if ufid is not None:
1296                ufid_str = (
1297                    "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(
1298                        ufid[0],
1299                        ufid[1] >> 16,
1300                        ufid[1] & 0xFFFF,
1301                        ufid[2] >> 16,
1302                        ufid[2] & 0,
1303                        ufid[3],
1304                    )
1305                )
1306
1307            key_field = self.get_attr("OVS_FLOW_ATTR_KEY")
1308            keymsg = None
1309            if key_field is not None:
1310                keymsg = key_field
1311
1312            mask_field = self.get_attr("OVS_FLOW_ATTR_MASK")
1313            maskmsg = None
1314            if mask_field is not None:
1315                maskmsg = mask_field
1316
1317            acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS")
1318            actsmsg = None
1319            if acts_field is not None:
1320                actsmsg = acts_field
1321
1322            print_str = ""
1323
1324            if more:
1325                print_str += ufid_str + ","
1326
1327            if keymsg is not None:
1328                print_str += keymsg.dpstr(maskmsg, more)
1329
1330            stats = self.get_attr("OVS_FLOW_ATTR_STATS")
1331            if stats is None:
1332                print_str += " packets:0, bytes:0,"
1333            else:
1334                print_str += " packets:%d, bytes:%d," % (
1335                    stats["packets"],
1336                    stats["bytes"],
1337                )
1338
1339            used = self.get_attr("OVS_FLOW_ATTR_USED")
1340            print_str += " used:"
1341            if used is None:
1342                print_str += "never,"
1343            else:
1344                used_time = int(used)
1345                cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC)
1346                used_time = (cur_time_sec * 1000) - used_time
1347                print_str += "{}s,".format(used_time / 1000)
1348
1349            print_str += " actions:"
1350            if (
1351                actsmsg is None
1352                or "attrs" not in actsmsg
1353                or len(actsmsg["attrs"]) == 0
1354            ):
1355                print_str += "drop"
1356            else:
1357                print_str += actsmsg.dpstr(more)
1358
1359            return print_str
1360
1361    def __init__(self):
1362        GenericNetlinkSocket.__init__(self)
1363
1364        self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg)
1365
1366    def dump(self, dpifindex, flowspec=None):
1367        """
1368        Returns a list of messages containing flows.
1369
1370        dpifindex should be a valid datapath obtained by calling
1371        into the OvsDatapath lookup
1372
1373        flowpsec is a string which represents a flow in the dpctl
1374        format.
1375        """
1376        msg = OvsFlow.ovs_flow_msg()
1377
1378        msg["cmd"] = OVS_FLOW_CMD_GET
1379        msg["version"] = OVS_DATAPATH_VERSION
1380        msg["reserved"] = 0
1381        msg["dpifindex"] = dpifindex
1382
1383        msg_flags = NLM_F_REQUEST | NLM_F_ACK
1384        if flowspec is None:
1385            msg_flags |= NLM_F_DUMP
1386        rep = None
1387
1388        try:
1389            rep = self.nlm_request(
1390                msg,
1391                msg_type=self.prid,
1392                msg_flags=msg_flags,
1393            )
1394        except NetlinkError as ne:
1395            raise ne
1396        return rep
1397
1398    def miss(self, packetmsg):
1399        seq = packetmsg["header"]["sequence_number"]
1400        keystr = "(none)"
1401        key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY")
1402        if key_field is not None:
1403            keystr = key_field.dpstr(None, True)
1404
1405        pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET")
1406        pktpres = "yes" if pktdata is not None else "no"
1407
1408        print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True)
1409
1410    def execute(self, packetmsg):
1411        print("userspace execute command")
1412
1413    def action(self, packetmsg):
1414        print("userspace action command")
1415
1416
1417def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
1418    dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
1419    base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
1420    megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")
1421    user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES")
1422    masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE")
1423
1424    print("%s:" % dp_name)
1425    print(
1426        "  lookups: hit:%d missed:%d lost:%d"
1427        % (base_stats["hit"], base_stats["missed"], base_stats["lost"])
1428    )
1429    print("  flows:%d" % base_stats["flows"])
1430    pkts = base_stats["hit"] + base_stats["missed"]
1431    avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0
1432    print(
1433        "  masks: hit:%d total:%d hit/pkt:%f"
1434        % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg)
1435    )
1436    print("  caches:")
1437    print("    masks-cache: size:%d" % masks_cache_size)
1438
1439    if user_features is not None:
1440        print("  features: 0x%X" % user_features)
1441
1442    # port print out
1443    for iface in ndb.interfaces:
1444        rep = vpl.info(iface.ifname, ifindex)
1445        if rep is not None:
1446            print(
1447                "  port %d: %s (%s)"
1448                % (
1449                    rep.get_attr("OVS_VPORT_ATTR_PORT_NO"),
1450                    rep.get_attr("OVS_VPORT_ATTR_NAME"),
1451                    OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")),
1452                )
1453            )
1454
1455
1456def main(argv):
1457    nlmsg_atoms.ovskey = ovskey
1458    nlmsg_atoms.ovsactions = ovsactions
1459
1460    parser = argparse.ArgumentParser()
1461    parser.add_argument(
1462        "-v",
1463        "--verbose",
1464        action="count",
1465        help="Increment 'verbose' output counter.",
1466        default=0,
1467    )
1468    subparsers = parser.add_subparsers()
1469
1470    showdpcmd = subparsers.add_parser("show")
1471    showdpcmd.add_argument(
1472        "showdp", metavar="N", type=str, nargs="?", help="Datapath Name"
1473    )
1474
1475    adddpcmd = subparsers.add_parser("add-dp")
1476    adddpcmd.add_argument("adddp", help="Datapath Name")
1477    adddpcmd.add_argument(
1478        "-u",
1479        "--upcall",
1480        action="store_true",
1481        help="Leave open a reader for upcalls",
1482    )
1483    adddpcmd.add_argument(
1484        "-V",
1485        "--versioning",
1486        required=False,
1487        help="Specify a custom version / feature string",
1488    )
1489
1490    deldpcmd = subparsers.add_parser("del-dp")
1491    deldpcmd.add_argument("deldp", help="Datapath Name")
1492
1493    addifcmd = subparsers.add_parser("add-if")
1494    addifcmd.add_argument("dpname", help="Datapath Name")
1495    addifcmd.add_argument("addif", help="Interface name for adding")
1496    addifcmd.add_argument(
1497        "-u",
1498        "--upcall",
1499        action="store_true",
1500        help="Leave open a reader for upcalls",
1501    )
1502    addifcmd.add_argument(
1503        "-t",
1504        "--ptype",
1505        type=str,
1506        default="netdev",
1507        choices=["netdev", "internal"],
1508        help="Interface type (default netdev)",
1509    )
1510    delifcmd = subparsers.add_parser("del-if")
1511    delifcmd.add_argument("dpname", help="Datapath Name")
1512    delifcmd.add_argument("delif", help="Interface name for adding")
1513
1514    dumpflcmd = subparsers.add_parser("dump-flows")
1515    dumpflcmd.add_argument("dumpdp", help="Datapath Name")
1516
1517    args = parser.parse_args()
1518
1519    if args.verbose > 0:
1520        if args.verbose > 1:
1521            logging.basicConfig(level=logging.DEBUG)
1522
1523    ovspk = OvsPacket()
1524    ovsdp = OvsDatapath()
1525    ovsvp = OvsVport(ovspk)
1526    ovsflow = OvsFlow()
1527    ndb = NDB()
1528
1529    if hasattr(args, "showdp"):
1530        found = False
1531        for iface in ndb.interfaces:
1532            rep = None
1533            if args.showdp is None:
1534                rep = ovsdp.info(iface.ifname, 0)
1535            elif args.showdp == iface.ifname:
1536                rep = ovsdp.info(iface.ifname, 0)
1537
1538            if rep is not None:
1539                found = True
1540                print_ovsdp_full(rep, iface.index, ndb, ovsvp)
1541
1542        if not found:
1543            msg = "No DP found"
1544            if args.showdp is not None:
1545                msg += ":'%s'" % args.showdp
1546            print(msg)
1547    elif hasattr(args, "adddp"):
1548        rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk)
1549        if rep is None:
1550            print("DP '%s' already exists" % args.adddp)
1551        else:
1552            print("DP '%s' added" % args.adddp)
1553        if args.upcall:
1554            ovspk.upcall_handler(ovsflow)
1555    elif hasattr(args, "deldp"):
1556        ovsdp.destroy(args.deldp)
1557    elif hasattr(args, "addif"):
1558        rep = ovsdp.info(args.dpname, 0)
1559        if rep is None:
1560            print("DP '%s' not found." % args.dpname)
1561            return 1
1562        dpindex = rep["dpifindex"]
1563        rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype)
1564        msg = "vport '%s'" % args.addif
1565        if rep and rep["header"]["error"] is None:
1566            msg += " added."
1567        else:
1568            msg += " failed to add."
1569        if args.upcall:
1570            if rep is None:
1571                rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk)
1572            ovsvp.upcall_handler(ovsflow)
1573    elif hasattr(args, "delif"):
1574        rep = ovsdp.info(args.dpname, 0)
1575        if rep is None:
1576            print("DP '%s' not found." % args.dpname)
1577            return 1
1578        rep = ovsvp.detach(rep["dpifindex"], args.delif)
1579        msg = "vport '%s'" % args.delif
1580        if rep and rep["header"]["error"] is None:
1581            msg += " removed."
1582        else:
1583            msg += " failed to remove."
1584    elif hasattr(args, "dumpdp"):
1585        rep = ovsdp.info(args.dumpdp, 0)
1586        if rep is None:
1587            print("DP '%s' not found." % args.dumpdp)
1588            return 1
1589        rep = ovsflow.dump(rep["dpifindex"])
1590        for flow in rep:
1591            print(flow.dpstr(True if args.verbose > 0 else False))
1592
1593    return 0
1594
1595
1596if __name__ == "__main__":
1597    sys.exit(main(sys.argv))
1598