xref: /linux/tools/testing/selftests/net/openvswitch/ovs-dpctl.py (revision 05416ada37aa4efe93f25b0532f551d424fb7b3d)
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 math
12import multiprocessing
13import re
14import struct
15import sys
16import time
17import types
18import uuid
19
20try:
21    from pyroute2 import NDB
22
23    from pyroute2.netlink import NLA_F_NESTED
24    from pyroute2.netlink import NLM_F_ACK
25    from pyroute2.netlink import NLM_F_DUMP
26    from pyroute2.netlink import NLM_F_REQUEST
27    from pyroute2.netlink import genlmsg
28    from pyroute2.netlink import nla
29    from pyroute2.netlink import nlmsg_atoms
30    from pyroute2.netlink.event import EventSocket
31    from pyroute2.netlink.exceptions import NetlinkError
32    from pyroute2.netlink.generic import GenericNetlinkSocket
33    from pyroute2.netlink.nlsocket import Marshal
34    import pyroute2
35    import pyroute2.iproute
36
37except ModuleNotFoundError:
38    print("Need to install the python pyroute2 package >= 0.6.")
39    sys.exit(1)
40
41
42OVS_DATAPATH_FAMILY = "ovs_datapath"
43OVS_VPORT_FAMILY = "ovs_vport"
44OVS_FLOW_FAMILY = "ovs_flow"
45OVS_PACKET_FAMILY = "ovs_packet"
46OVS_METER_FAMILY = "ovs_meter"
47OVS_CT_LIMIT_FAMILY = "ovs_ct_limit"
48
49OVS_DATAPATH_VERSION = 2
50OVS_DP_CMD_NEW = 1
51OVS_DP_CMD_DEL = 2
52OVS_DP_CMD_GET = 3
53OVS_DP_CMD_SET = 4
54
55OVS_VPORT_CMD_NEW = 1
56OVS_VPORT_CMD_DEL = 2
57OVS_VPORT_CMD_GET = 3
58OVS_VPORT_CMD_SET = 4
59
60OVS_FLOW_CMD_NEW = 1
61OVS_FLOW_CMD_DEL = 2
62OVS_FLOW_CMD_GET = 3
63OVS_FLOW_CMD_SET = 4
64
65UINT32_MAX = 0xFFFFFFFF
66
67def macstr(mac):
68    outstr = ":".join(["%02X" % i for i in mac])
69    return outstr
70
71
72def strcspn(str1, str2):
73    tot = 0
74    for char in str1:
75        if str2.find(char) != -1:
76            return tot
77        tot += 1
78    return tot
79
80
81def strspn(str1, str2):
82    tot = 0
83    for char in str1:
84        if str2.find(char) == -1:
85            return tot
86        tot += 1
87    return tot
88
89
90def intparse(statestr, defmask="0xffffffff"):
91    totalparse = strspn(statestr, "0123456789abcdefABCDEFx/")
92    # scan until "/"
93    count = strspn(statestr, "x0123456789abcdefABCDEF")
94
95    firstnum = statestr[:count]
96    if firstnum[-1] == "/":
97        firstnum = firstnum[:-1]
98    k = int(firstnum, 0)
99
100    m = None
101    if defmask is not None:
102        secondnum = defmask
103        if statestr[count] == "/":
104            secondnum = statestr[count + 1 :]  # this is wrong...
105        m = int(secondnum, 0)
106
107    return statestr[totalparse + 1 :], k, m
108
109
110def parse_flags(flag_str, flag_vals):
111    bitResult = 0
112    maskResult = 0
113
114    if len(flag_str) == 0:
115        return flag_str, bitResult, maskResult
116
117    if flag_str[0].isdigit():
118        idx = 0
119        while flag_str[idx].isdigit() or flag_str[idx] == "x":
120            idx += 1
121        digits = flag_str[:idx]
122        flag_str = flag_str[idx:]
123
124        bitResult = int(digits, 0)
125        maskResult = int(digits, 0)
126
127    while len(flag_str) > 0 and (flag_str[0] == "+" or flag_str[0] == "-"):
128        if flag_str[0] == "+":
129            setFlag = True
130        elif flag_str[0] == "-":
131            setFlag = False
132
133        flag_str = flag_str[1:]
134
135        flag_len = 0
136        while (
137            flag_str[flag_len] != "+"
138            and flag_str[flag_len] != "-"
139            and flag_str[flag_len] != ","
140            and flag_str[flag_len] != ")"
141        ):
142            flag_len += 1
143
144        flag = flag_str[0:flag_len]
145
146        if flag in flag_vals:
147            if maskResult & flag_vals[flag]:
148                raise KeyError(
149                    "Flag %s set once, cannot be set in multiples" % flag
150                )
151
152            if setFlag:
153                bitResult |= flag_vals[flag]
154
155            maskResult |= flag_vals[flag]
156        else:
157            raise KeyError("Missing flag value: %s" % flag)
158
159        flag_str = flag_str[flag_len:]
160
161    return flag_str, bitResult, maskResult
162
163
164def parse_ct_state(statestr):
165    ct_flags = {
166        "new": 1 << 0,
167        "est": 1 << 1,
168        "rel": 1 << 2,
169        "rpl": 1 << 3,
170        "inv": 1 << 4,
171        "trk": 1 << 5,
172        "snat": 1 << 6,
173        "dnat": 1 << 7,
174    }
175
176    return parse_flags(statestr, ct_flags)
177
178
179def convert_mac(data):
180    def to_bytes(mac):
181        mac_split = mac.split(":")
182        ret = bytearray([int(i, 16) for i in mac_split])
183        return bytes(ret)
184
185    mac_str, _, mask_str = data.partition('/')
186
187    if not mac_str:
188        mac_str = mask_str = "00:00:00:00:00:00"
189    elif not mask_str:
190        mask_str = "FF:FF:FF:FF:FF:FF"
191
192    return to_bytes(mac_str), to_bytes(mask_str)
193
194def convert_ipv4(data):
195    ip, _, mask = data.partition('/')
196
197    if not ip:
198        ip = mask = 0
199    elif not mask:
200        mask = 0xFFFFFFFF
201    elif mask.isdigit():
202        mask = (0xFFFFFFFF << (32 - int(mask))) & 0xFFFFFFFF
203
204    return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask))
205
206def convert_ipv6(data):
207    ip, _, mask = data.partition('/')
208
209    if not ip:
210        ip = mask = 0
211    elif not mask:
212        mask = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
213    elif mask.isdigit():
214        mask = ipaddress.IPv6Network("::/" + mask).hostmask
215
216    return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed
217
218def convert_int(size):
219    def convert_int_sized(data):
220        value, _, mask = data.partition('/')
221
222        if not value:
223            return 0, 0
224        elif not mask:
225            return int(value, 0), pow(2, size) - 1
226        else:
227            return int(value, 0), int(mask, 0)
228
229    return convert_int_sized
230
231def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False):
232    if scanregex:
233        m = re.search(scanstr, block_str)
234        if m is None:
235            if returnskipped:
236                return block_str
237            return False
238        if returnskipped:
239            block_str = block_str[len(m.group(0)) :]
240            return block_str
241        return True
242
243    if block_str.startswith(scanstr):
244        if returnskipped:
245            block_str = block_str[len(scanstr) :]
246        else:
247            return True
248
249    if returnskipped:
250        return block_str
251
252    return False
253
254
255def parse_extract_field(
256    block_str, fieldstr, scanfmt, convert, masked=False, defval=None
257):
258    if fieldstr and not block_str.startswith(fieldstr):
259        return block_str, defval
260
261    if fieldstr:
262        str_skiplen = len(fieldstr)
263        str_skipped = block_str[str_skiplen:]
264        if str_skiplen == 0:
265            return str_skipped, defval
266    else:
267        str_skiplen = 0
268        str_skipped = block_str
269
270    m = re.search(scanfmt, str_skipped)
271    if m is None:
272        raise ValueError("Bad fmt string")
273
274    data = m.group(0)
275    if convert:
276        data = convert(m.group(0))
277
278    str_skipped = str_skipped[len(m.group(0)) :]
279    if masked:
280        if str_skipped[0] == "/":
281            raise ValueError("Masking support TBD...")
282
283    str_skipped = str_skipped[strspn(str_skipped, ", ") :]
284    return str_skipped, data
285
286
287def parse_attrs(actstr, attr_desc):
288    """Parses the given action string and returns a list of netlink
289    attributes based on a list of attribute descriptions.
290
291    Each element in the attribute description list is a tuple such as:
292        (name, attr_name, parse_func)
293    where:
294        name: is the string representing the attribute
295        attr_name: is the name of the attribute as defined in the uAPI.
296        parse_func: is a callable accepting a string and returning either
297            a single object (the parsed attribute value) or a tuple of
298            two values (the parsed attribute value and the remaining string)
299
300    Returns a list of attributes and the remaining string.
301    """
302    def parse_attr(actstr, key, func):
303        actstr = actstr[len(key) :]
304
305        if not func:
306            return None, actstr
307
308        delim = actstr[0]
309        actstr = actstr[1:]
310
311        if delim == "=":
312            pos = strcspn(actstr, ",)")
313            ret = func(actstr[:pos])
314        else:
315            ret = func(actstr)
316
317        if isinstance(ret, tuple):
318            (datum, actstr) = ret
319        else:
320            datum = ret
321            actstr = actstr[strcspn(actstr, ",)"):]
322
323        if delim == "(":
324            if not actstr or actstr[0] != ")":
325                raise ValueError("Action contains unbalanced parentheses")
326
327            actstr = actstr[1:]
328
329        actstr = actstr[strspn(actstr, ", ") :]
330
331        return datum, actstr
332
333    attrs = []
334    attr_desc = list(attr_desc)
335    while actstr and actstr[0] != ")" and attr_desc:
336        found = False
337        for i, (key, attr, func) in enumerate(attr_desc):
338            if actstr.startswith(key):
339                datum, actstr = parse_attr(actstr, key, func)
340                attrs.append([attr, datum])
341                found = True
342                del attr_desc[i]
343
344        if not found:
345            raise ValueError("Unknown attribute: '%s'" % actstr)
346
347        actstr = actstr[strspn(actstr, ", ") :]
348
349    if actstr[0] != ")":
350        raise ValueError("Action string contains extra garbage or has "
351                         "unbalanced parenthesis: '%s'" % actstr)
352
353    return attrs, actstr[1:]
354
355
356class ovs_dp_msg(genlmsg):
357    # include the OVS version
358    # We need a custom header rather than just being able to rely on
359    # genlmsg because fields ends up not expressing everything correctly
360    # if we use the canonical example of setting fields = (('customfield',),)
361    fields = genlmsg.fields + (("dpifindex", "I"),)
362
363
364class ovsactions(nla):
365    nla_flags = NLA_F_NESTED
366
367    nla_map = (
368        ("OVS_ACTION_ATTR_UNSPEC", "none"),
369        ("OVS_ACTION_ATTR_OUTPUT", "uint32"),
370        ("OVS_ACTION_ATTR_USERSPACE", "userspace"),
371        ("OVS_ACTION_ATTR_SET", "ovskey"),
372        ("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
373        ("OVS_ACTION_ATTR_POP_VLAN", "flag"),
374        ("OVS_ACTION_ATTR_SAMPLE", "sample"),
375        ("OVS_ACTION_ATTR_RECIRC", "uint32"),
376        ("OVS_ACTION_ATTR_HASH", "none"),
377        ("OVS_ACTION_ATTR_PUSH_MPLS", "none"),
378        ("OVS_ACTION_ATTR_POP_MPLS", "flag"),
379        ("OVS_ACTION_ATTR_SET_MASKED", "ovskey"),
380        ("OVS_ACTION_ATTR_CT", "ctact"),
381        ("OVS_ACTION_ATTR_TRUNC", "uint32"),
382        ("OVS_ACTION_ATTR_PUSH_ETH", "none"),
383        ("OVS_ACTION_ATTR_POP_ETH", "flag"),
384        ("OVS_ACTION_ATTR_CT_CLEAR", "flag"),
385        ("OVS_ACTION_ATTR_PUSH_NSH", "none"),
386        ("OVS_ACTION_ATTR_POP_NSH", "flag"),
387        ("OVS_ACTION_ATTR_METER", "none"),
388        ("OVS_ACTION_ATTR_CLONE", "recursive"),
389        ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"),
390        ("OVS_ACTION_ATTR_ADD_MPLS", "none"),
391        ("OVS_ACTION_ATTR_DEC_TTL", "none"),
392        ("OVS_ACTION_ATTR_DROP", "uint32"),
393        ("OVS_ACTION_ATTR_PSAMPLE", "psample"),
394    )
395
396    class psample(nla):
397        nla_flags = NLA_F_NESTED
398
399        nla_map = (
400            ("OVS_PSAMPLE_ATTR_UNSPEC", "none"),
401            ("OVS_PSAMPLE_ATTR_GROUP", "uint32"),
402            ("OVS_PSAMPLE_ATTR_COOKIE", "array(uint8)"),
403        )
404
405        def dpstr(self, more=False):
406            args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP")
407
408            cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE")
409            if cookie:
410                args += ",cookie(%s)" % \
411                        "".join(format(x, "02x") for x in cookie)
412
413            return "psample(%s)" % args
414
415        def parse(self, actstr):
416            desc = (
417                ("group", "OVS_PSAMPLE_ATTR_GROUP", int),
418                ("cookie", "OVS_PSAMPLE_ATTR_COOKIE",
419                    lambda x: list(bytearray.fromhex(x)))
420            )
421
422            attrs, actstr = parse_attrs(actstr, desc)
423
424            for attr in attrs:
425                self["attrs"].append(attr)
426
427            return actstr
428
429    class sample(nla):
430        nla_flags = NLA_F_NESTED
431
432        nla_map = (
433            ("OVS_SAMPLE_ATTR_UNSPEC", "none"),
434            ("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"),
435            ("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"),
436        )
437
438        def dpstr(self, more=False):
439            args = []
440
441            args.append("sample={:.2f}%".format(
442                100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") /
443                UINT32_MAX))
444
445            actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS")
446            if actions:
447                args.append("actions(%s)" % actions.dpstr(more))
448
449            return "sample(%s)" % ",".join(args)
450
451        def parse(self, actstr):
452            def parse_nested_actions(actstr):
453                subacts = ovsactions()
454                parsed_len = subacts.parse(actstr)
455                return subacts, actstr[parsed_len :]
456
457            def percent_to_rate(percent):
458                percent = float(percent.strip('%'))
459                return int(math.floor(UINT32_MAX * (percent / 100.0) + .5))
460
461            desc = (
462                ("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate),
463                ("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions),
464            )
465            attrs, actstr = parse_attrs(actstr, desc)
466
467            for attr in attrs:
468                self["attrs"].append(attr)
469
470            return actstr
471
472    class ctact(nla):
473        nla_flags = NLA_F_NESTED
474
475        nla_map = (
476            ("OVS_CT_ATTR_NONE", "none"),
477            ("OVS_CT_ATTR_COMMIT", "flag"),
478            ("OVS_CT_ATTR_ZONE", "uint16"),
479            ("OVS_CT_ATTR_MARK", "none"),
480            ("OVS_CT_ATTR_LABELS", "none"),
481            ("OVS_CT_ATTR_HELPER", "asciiz"),
482            ("OVS_CT_ATTR_NAT", "natattr"),
483            ("OVS_CT_ATTR_FORCE_COMMIT", "flag"),
484            ("OVS_CT_ATTR_EVENTMASK", "uint32"),
485            ("OVS_CT_ATTR_TIMEOUT", "asciiz"),
486        )
487
488        class natattr(nla):
489            nla_flags = NLA_F_NESTED
490
491            nla_map = (
492                ("OVS_NAT_ATTR_NONE", "none"),
493                ("OVS_NAT_ATTR_SRC", "flag"),
494                ("OVS_NAT_ATTR_DST", "flag"),
495                ("OVS_NAT_ATTR_IP_MIN", "ipaddr"),
496                ("OVS_NAT_ATTR_IP_MAX", "ipaddr"),
497                ("OVS_NAT_ATTR_PROTO_MIN", "uint16"),
498                ("OVS_NAT_ATTR_PROTO_MAX", "uint16"),
499                ("OVS_NAT_ATTR_PERSISTENT", "flag"),
500                ("OVS_NAT_ATTR_PROTO_HASH", "flag"),
501                ("OVS_NAT_ATTR_PROTO_RANDOM", "flag"),
502            )
503
504            def dpstr(self, more=False):
505                print_str = "nat("
506
507                if self.get_attr("OVS_NAT_ATTR_SRC"):
508                    print_str += "src"
509                elif self.get_attr("OVS_NAT_ATTR_DST"):
510                    print_str += "dst"
511                else:
512                    print_str += "XXX-unknown-nat"
513
514                if self.get_attr("OVS_NAT_ATTR_IP_MIN") or self.get_attr(
515                    "OVS_NAT_ATTR_IP_MAX"
516                ):
517                    if self.get_attr("OVS_NAT_ATTR_IP_MIN"):
518                        print_str += "=%s," % str(
519                            self.get_attr("OVS_NAT_ATTR_IP_MIN")
520                        )
521
522                    if self.get_attr("OVS_NAT_ATTR_IP_MAX"):
523                        print_str += "-%s," % str(
524                            self.get_attr("OVS_NAT_ATTR_IP_MAX")
525                        )
526                else:
527                    print_str += ","
528
529                if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"):
530                    print_str += "proto_min=%d," % self.get_attr(
531                        "OVS_NAT_ATTR_PROTO_MIN"
532                    )
533
534                if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"):
535                    print_str += "proto_max=%d," % self.get_attr(
536                        "OVS_NAT_ATTR_PROTO_MAX"
537                    )
538
539                if self.get_attr("OVS_NAT_ATTR_PERSISTENT"):
540                    print_str += "persistent,"
541                if self.get_attr("OVS_NAT_ATTR_HASH"):
542                    print_str += "hash,"
543                if self.get_attr("OVS_NAT_ATTR_RANDOM"):
544                    print_str += "random"
545                print_str += ")"
546                return print_str
547
548        def dpstr(self, more=False):
549            print_str = "ct("
550
551            if self.get_attr("OVS_CT_ATTR_COMMIT") is not None:
552                print_str += "commit,"
553            if self.get_attr("OVS_CT_ATTR_ZONE") is not None:
554                print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE")
555            if self.get_attr("OVS_CT_ATTR_HELPER") is not None:
556                print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER")
557            if self.get_attr("OVS_CT_ATTR_NAT") is not None:
558                print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more)
559                print_str += ","
560            if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None:
561                print_str += "force,"
562            if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None:
563                print_str += "emask=0x%X," % self.get_attr(
564                    "OVS_CT_ATTR_EVENTMASK"
565                )
566            if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None:
567                print_str += "timeout=%s" % self.get_attr(
568                    "OVS_CT_ATTR_TIMEOUT"
569                )
570            print_str += ")"
571            return print_str
572
573    class userspace(nla):
574        nla_flags = NLA_F_NESTED
575
576        nla_map = (
577            ("OVS_USERSPACE_ATTR_UNUSED", "none"),
578            ("OVS_USERSPACE_ATTR_PID", "uint32"),
579            ("OVS_USERSPACE_ATTR_USERDATA", "array(uint8)"),
580            ("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", "uint32"),
581        )
582
583        def dpstr(self, more=False):
584            print_str = "userspace("
585            if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None:
586                print_str += "pid=%d," % self.get_attr(
587                    "OVS_USERSPACE_ATTR_PID"
588                )
589            if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None:
590                print_str += "userdata="
591                for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"):
592                    print_str += "%x." % f
593            if self.get_attr("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT") is not None:
594                print_str += "egress_tun_port=%d" % self.get_attr(
595                    "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT"
596                )
597            print_str += ")"
598            return print_str
599
600        def parse(self, actstr):
601            attrs_desc = (
602                ("pid", "OVS_USERSPACE_ATTR_PID", int),
603                ("userdata", "OVS_USERSPACE_ATTR_USERDATA",
604                    lambda x: list(bytearray.fromhex(x))),
605                ("egress_tun_port", "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", int)
606            )
607
608            attrs, actstr = parse_attrs(actstr, attrs_desc)
609            for attr in attrs:
610                self["attrs"].append(attr)
611
612            return actstr
613
614    def dpstr(self, more=False):
615        print_str = ""
616
617        for field in self["attrs"]:
618            if field[1] == "none" or self.get_attr(field[0]) is None:
619                continue
620            if print_str != "":
621                print_str += ","
622
623            if field[0] == "OVS_ACTION_ATTR_OUTPUT":
624                print_str += "%d" % int(self.get_attr(field[0]))
625            elif field[0] == "OVS_ACTION_ATTR_RECIRC":
626                print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))
627            elif field[0] == "OVS_ACTION_ATTR_TRUNC":
628                print_str += "trunc(%d)" % int(self.get_attr(field[0]))
629            elif field[0] == "OVS_ACTION_ATTR_DROP":
630                print_str += "drop(%d)" % int(self.get_attr(field[0]))
631            elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR":
632                print_str += "ct_clear"
633            elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":
634                print_str += "pop_vlan"
635            elif field[0] == "OVS_ACTION_ATTR_POP_ETH":
636                print_str += "pop_eth"
637            elif field[0] == "OVS_ACTION_ATTR_POP_NSH":
638                print_str += "pop_nsh"
639            elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":
640                print_str += "pop_mpls"
641            else:
642                datum = self.get_attr(field[0])
643                if field[0] == "OVS_ACTION_ATTR_CLONE":
644                    print_str += "clone("
645                    print_str += datum.dpstr(more)
646                    print_str += ")"
647                elif field[0] == "OVS_ACTION_ATTR_SET" or \
648                     field[0] == "OVS_ACTION_ATTR_SET_MASKED":
649                    print_str += "set"
650                    field = datum
651                    mask = None
652                    if field[0] == "OVS_ACTION_ATTR_SET_MASKED":
653                        print_str += "_masked"
654                        field = datum[0]
655                        mask = datum[1]
656                    print_str += "("
657                    print_str += field.dpstr(mask, more)
658                    print_str += ")"
659                else:
660                    try:
661                        print_str += datum.dpstr(more)
662                    except:
663                        print_str += "{ATTR: %s not decoded}" % field[0]
664
665        return print_str
666
667    def parse(self, actstr):
668        totallen = len(actstr)
669        while len(actstr) != 0:
670            parsed = False
671            parencount = 0
672            if actstr.startswith("drop"):
673                # If no reason is provided, the implicit drop is used (i.e no
674                # action). If some reason is given, an explicit action is used.
675                reason = None
676                if actstr.startswith("drop("):
677                    parencount += 1
678
679                    actstr, reason = parse_extract_field(
680                        actstr,
681                        "drop(",
682                        r"([0-9]+)",
683                        lambda x: int(x, 0),
684                        False,
685                        None,
686                    )
687
688                if reason is not None:
689                    self["attrs"].append(["OVS_ACTION_ATTR_DROP", reason])
690                    parsed = True
691                else:
692                    actstr = actstr[len("drop"): ]
693                    return (totallen - len(actstr))
694
695            elif parse_starts_block(actstr, r"^(\d+)", False, True):
696                actstr, output = parse_extract_field(
697                    actstr, None, r"(\d+)", lambda x: int(x), False, "0"
698                )
699                self["attrs"].append(["OVS_ACTION_ATTR_OUTPUT", output])
700                parsed = True
701            elif parse_starts_block(actstr, "recirc(", False):
702                actstr, recircid = parse_extract_field(
703                    actstr,
704                    "recirc(",
705                    r"([0-9a-fA-Fx]+)",
706                    lambda x: int(x, 0),
707                    False,
708                    0,
709                )
710                parencount += 1
711                self["attrs"].append(["OVS_ACTION_ATTR_RECIRC", recircid])
712                parsed = True
713
714            parse_flat_map = (
715                ("ct_clear", "OVS_ACTION_ATTR_CT_CLEAR"),
716                ("pop_vlan", "OVS_ACTION_ATTR_POP_VLAN"),
717                ("pop_eth", "OVS_ACTION_ATTR_POP_ETH"),
718                ("pop_nsh", "OVS_ACTION_ATTR_POP_NSH"),
719            )
720
721            for flat_act in parse_flat_map:
722                if parse_starts_block(actstr, flat_act[0], False):
723                    actstr = actstr[len(flat_act[0]):]
724                    self["attrs"].append([flat_act[1], True])
725                    actstr = actstr[strspn(actstr, ", ") :]
726                    parsed = True
727
728            if parse_starts_block(actstr, "clone(", False):
729                parencount += 1
730                subacts = ovsactions()
731                actstr = actstr[len("clone("):]
732                parsedLen = subacts.parse(actstr)
733                lst = []
734                self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts))
735                actstr = actstr[parsedLen:]
736                parsed = True
737            elif parse_starts_block(actstr, "set(", False):
738                parencount += 1
739                k = ovskey()
740                actstr = actstr[len("set("):]
741                actstr = k.parse(actstr, None)
742                self["attrs"].append(("OVS_ACTION_ATTR_SET", k))
743                if not actstr.startswith(")"):
744                    actstr = ")" + actstr
745                parsed = True
746            elif parse_starts_block(actstr, "set_masked(", False):
747                parencount += 1
748                k = ovskey()
749                m = ovskey()
750                actstr = actstr[len("set_masked("):]
751                actstr = k.parse(actstr, m)
752                self["attrs"].append(("OVS_ACTION_ATTR_SET_MASKED", [k, m]))
753                if not actstr.startswith(")"):
754                    actstr = ")" + actstr
755                parsed = True
756            elif parse_starts_block(actstr, "ct(", False):
757                parencount += 1
758                actstr = actstr[len("ct(") :]
759                ctact = ovsactions.ctact()
760
761                for scan in (
762                    ("commit", "OVS_CT_ATTR_COMMIT", None),
763                    ("force_commit", "OVS_CT_ATTR_FORCE_COMMIT", None),
764                    ("zone", "OVS_CT_ATTR_ZONE", int),
765                    ("mark", "OVS_CT_ATTR_MARK", int),
766                    ("helper", "OVS_CT_ATTR_HELPER", lambda x, y: str(x)),
767                    ("timeout", "OVS_CT_ATTR_TIMEOUT", lambda x, y: str(x)),
768                ):
769                    if actstr.startswith(scan[0]):
770                        actstr = actstr[len(scan[0]) :]
771                        if scan[2] is not None:
772                            if actstr[0] != "=":
773                                raise ValueError("Invalid ct attr")
774                            actstr = actstr[1:]
775                            pos = strcspn(actstr, ",)")
776                            datum = scan[2](actstr[:pos], 0)
777                            ctact["attrs"].append([scan[1], datum])
778                            actstr = actstr[pos:]
779                        else:
780                            ctact["attrs"].append([scan[1], None])
781                        actstr = actstr[strspn(actstr, ", ") :]
782                    # it seems strange to put this here, but nat() is a complex
783                    # sub-action and this lets it sit anywhere in the ct() action
784                    if actstr.startswith("nat"):
785                        actstr = actstr[3:]
786                        natact = ovsactions.ctact.natattr()
787
788                        if actstr.startswith("("):
789                            parencount += 1
790                            t = None
791                            actstr = actstr[1:]
792                            if actstr.startswith("src"):
793                                t = "OVS_NAT_ATTR_SRC"
794                                actstr = actstr[3:]
795                            elif actstr.startswith("dst"):
796                                t = "OVS_NAT_ATTR_DST"
797                                actstr = actstr[3:]
798
799                            actstr, ip_block_min = parse_extract_field(
800                                actstr, "=", r"([0-9a-fA-F\.]+)", str, False
801                            )
802                            actstr, ip_block_max = parse_extract_field(
803                                actstr, "-", r"([0-9a-fA-F\.]+)", str, False
804                            )
805
806                            actstr, proto_min = parse_extract_field(
807                                actstr, ":", r"(\d+)", int, False
808                            )
809                            actstr, proto_max = parse_extract_field(
810                                actstr, "-", r"(\d+)", int, False
811                            )
812
813                            if t is not None:
814                                natact["attrs"].append([t, None])
815
816                                if ip_block_min is not None:
817                                    natact["attrs"].append(
818                                        ["OVS_NAT_ATTR_IP_MIN", ip_block_min]
819                                    )
820                                if ip_block_max is not None:
821                                    natact["attrs"].append(
822                                        ["OVS_NAT_ATTR_IP_MAX", ip_block_max]
823                                    )
824                                if proto_min is not None:
825                                    natact["attrs"].append(
826                                        ["OVS_NAT_ATTR_PROTO_MIN", proto_min]
827                                    )
828                                if proto_max is not None:
829                                    natact["attrs"].append(
830                                        ["OVS_NAT_ATTR_PROTO_MAX", proto_max]
831                                    )
832
833                            for natscan in (
834                                ("persistent", "OVS_NAT_ATTR_PERSISTENT"),
835                                ("hash", "OVS_NAT_ATTR_PROTO_HASH"),
836                                ("random", "OVS_NAT_ATTR_PROTO_RANDOM"),
837                            ):
838                                if actstr.startswith(natscan[0]):
839                                    actstr = actstr[len(natscan[0]) :]
840                                    natact["attrs"].append([natscan[1], None])
841                                    actstr = actstr[strspn(actstr, ", ") :]
842
843                        ctact["attrs"].append(["OVS_CT_ATTR_NAT", natact])
844                        actstr = actstr[strspn(actstr, ", ") :]
845
846                self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact])
847                parsed = True
848
849            elif parse_starts_block(actstr, "sample(", False):
850                sampleact = self.sample()
851                actstr = sampleact.parse(actstr[len("sample(") : ])
852                self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact])
853                parsed = True
854
855            elif parse_starts_block(actstr, "psample(", False):
856                psampleact = self.psample()
857                actstr = psampleact.parse(actstr[len("psample(") : ])
858                self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact])
859                parsed = True
860
861            elif parse_starts_block(actstr, "userspace(", False):
862                uact = self.userspace()
863                actstr = uact.parse(actstr[len("userspace(") : ])
864                self["attrs"].append(["OVS_ACTION_ATTR_USERSPACE", uact])
865                parsed = True
866
867            elif parse_starts_block(actstr, "trunc(", False):
868                parencount += 1
869                actstr, val = parse_extract_field(
870                    actstr,
871                    "trunc(",
872                    r"([0-9]+)",
873                    int,
874                    False,
875                    None,
876                )
877                self["attrs"].append(["OVS_ACTION_ATTR_TRUNC", val])
878                parsed = True
879
880            actstr = actstr[strspn(actstr, ", ") :]
881            while parencount > 0:
882                parencount -= 1
883                actstr = actstr[strspn(actstr, " "):]
884                if len(actstr) and actstr[0] != ")":
885                    raise ValueError("Action str: '%s' unbalanced" % actstr)
886                actstr = actstr[1:]
887
888            if len(actstr) and actstr[0] == ")":
889                return (totallen - len(actstr))
890
891            actstr = actstr[strspn(actstr, ", ") :]
892
893            if not parsed:
894                raise ValueError("Action str: '%s' not supported" % actstr)
895
896        return (totallen - len(actstr))
897
898
899class ovskey(nla):
900    nla_flags = NLA_F_NESTED
901    nla_map = (
902        ("OVS_KEY_ATTR_UNSPEC", "none"),
903        ("OVS_KEY_ATTR_ENCAP", "none"),
904        ("OVS_KEY_ATTR_PRIORITY", "uint32"),
905        ("OVS_KEY_ATTR_IN_PORT", "uint32"),
906        ("OVS_KEY_ATTR_ETHERNET", "ethaddr"),
907        ("OVS_KEY_ATTR_VLAN", "uint16"),
908        ("OVS_KEY_ATTR_ETHERTYPE", "be16"),
909        ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"),
910        ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"),
911        ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"),
912        ("OVS_KEY_ATTR_UDP", "ovs_key_udp"),
913        ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"),
914        ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"),
915        ("OVS_KEY_ATTR_ARP", "ovs_key_arp"),
916        ("OVS_KEY_ATTR_ND", "ovs_key_nd"),
917        ("OVS_KEY_ATTR_SKB_MARK", "uint32"),
918        ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"),
919        ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"),
920        ("OVS_KEY_ATTR_TCP_FLAGS", "be16"),
921        ("OVS_KEY_ATTR_DP_HASH", "uint32"),
922        ("OVS_KEY_ATTR_RECIRC_ID", "uint32"),
923        ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"),
924        ("OVS_KEY_ATTR_CT_STATE", "uint32"),
925        ("OVS_KEY_ATTR_CT_ZONE", "uint16"),
926        ("OVS_KEY_ATTR_CT_MARK", "uint32"),
927        ("OVS_KEY_ATTR_CT_LABELS", "none"),
928        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"),
929        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"),
930        ("OVS_KEY_ATTR_NSH", "none"),
931        ("OVS_KEY_ATTR_PACKET_TYPE", "none"),
932        ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"),
933        ("OVS_KEY_ATTR_TUNNEL_INFO", "none"),
934        ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"),
935    )
936
937    class ovs_key_proto(nla):
938        fields = (
939            ("src", "!H"),
940            ("dst", "!H"),
941        )
942
943        fields_map = (
944            ("src", "src", "%d", lambda x: int(x) if x else 0,
945                convert_int(16)),
946            ("dst", "dst", "%d", lambda x: int(x) if x else 0,
947                convert_int(16)),
948        )
949
950        def __init__(
951            self,
952            protostr,
953            data=None,
954            offset=None,
955            parent=None,
956            length=None,
957            init=None,
958        ):
959            self.proto_str = protostr
960            nla.__init__(
961                self,
962                data=data,
963                offset=offset,
964                parent=parent,
965                length=length,
966                init=init,
967            )
968
969        def parse(self, flowstr, typeInst):
970            if not flowstr.startswith(self.proto_str):
971                return None, None
972
973            k = typeInst()
974            m = typeInst()
975
976            flowstr = flowstr[len(self.proto_str) :]
977            if flowstr.startswith("("):
978                flowstr = flowstr[1:]
979
980            keybits = b""
981            maskbits = b""
982            for f in self.fields_map:
983                if flowstr.startswith(f[1]):
984                    # the following assumes that the field looks
985                    # something like 'field.' where '.' is a
986                    # character that we don't exactly care about.
987                    flowstr = flowstr[len(f[1]) + 1 :]
988                    splitchar = 0
989                    for c in flowstr:
990                        if c == "," or c == ")":
991                            break
992                        splitchar += 1
993                    data = flowstr[:splitchar]
994                    flowstr = flowstr[splitchar:]
995                else:
996                    data = ""
997
998                if len(f) > 4:
999                    k[f[0]], m[f[0]] = f[4](data)
1000                else:
1001                    k[f[0]] = f[3](data)
1002                    m[f[0]] = f[3](data)
1003
1004                flowstr = flowstr[strspn(flowstr, ", ") :]
1005                if len(flowstr) == 0:
1006                    return flowstr, k, m
1007
1008            flowstr = flowstr[strspn(flowstr, "), ") :]
1009
1010            return flowstr, k, m
1011
1012        def dpstr(self, masked=None, more=False):
1013            outstr = self.proto_str + "("
1014            first = False
1015            for f in self.fields_map:
1016                if first:
1017                    outstr += ","
1018                if masked is None:
1019                    outstr += "%s=" % f[0]
1020                    if isinstance(f[2], str):
1021                        outstr += f[2] % self[f[1]]
1022                    else:
1023                        outstr += f[2](self[f[1]])
1024                    first = True
1025                elif more or f[3](masked[f[1]]) != 0:
1026                    outstr += "%s=" % f[0]
1027                    if isinstance(f[2], str):
1028                        outstr += f[2] % self[f[1]]
1029                    else:
1030                        outstr += f[2](self[f[1]])
1031                    outstr += "/"
1032                    if isinstance(f[2], str):
1033                        outstr += f[2] % masked[f[1]]
1034                    else:
1035                        outstr += f[2](masked[f[1]])
1036                    first = True
1037            outstr += ")"
1038            return outstr
1039
1040    class ethaddr(ovs_key_proto):
1041        fields = (
1042            ("src", "!6s"),
1043            ("dst", "!6s"),
1044        )
1045
1046        fields_map = (
1047            (
1048                "src",
1049                "src",
1050                macstr,
1051                lambda x: int.from_bytes(x, "big"),
1052                convert_mac,
1053            ),
1054            (
1055                "dst",
1056                "dst",
1057                macstr,
1058                lambda x: int.from_bytes(x, "big"),
1059                convert_mac,
1060            ),
1061        )
1062
1063        def __init__(
1064            self,
1065            data=None,
1066            offset=None,
1067            parent=None,
1068            length=None,
1069            init=None,
1070        ):
1071            ovskey.ovs_key_proto.__init__(
1072                self,
1073                "eth",
1074                data=data,
1075                offset=offset,
1076                parent=parent,
1077                length=length,
1078                init=init,
1079            )
1080
1081    class ovs_key_ipv4(ovs_key_proto):
1082        fields = (
1083            ("src", "!I"),
1084            ("dst", "!I"),
1085            ("proto", "B"),
1086            ("tos", "B"),
1087            ("ttl", "B"),
1088            ("frag", "B"),
1089        )
1090
1091        fields_map = (
1092            (
1093                "src",
1094                "src",
1095                lambda x: str(ipaddress.IPv4Address(x)),
1096                int,
1097                convert_ipv4,
1098            ),
1099            (
1100                "dst",
1101                "dst",
1102                lambda x: str(ipaddress.IPv4Address(x)),
1103                int,
1104                convert_ipv4,
1105            ),
1106            ("proto", "proto", "%d", lambda x: int(x) if x else 0,
1107                convert_int(8)),
1108            ("tos", "tos", "%d", lambda x: int(x) if x else 0,
1109                convert_int(8)),
1110            ("ttl", "ttl", "%d", lambda x: int(x) if x else 0,
1111                convert_int(8)),
1112            ("frag", "frag", "%d", lambda x: int(x) if x else 0,
1113                convert_int(8)),
1114        )
1115
1116        def __init__(
1117            self,
1118            data=None,
1119            offset=None,
1120            parent=None,
1121            length=None,
1122            init=None,
1123        ):
1124            ovskey.ovs_key_proto.__init__(
1125                self,
1126                "ipv4",
1127                data=data,
1128                offset=offset,
1129                parent=parent,
1130                length=length,
1131                init=init,
1132            )
1133
1134    class ovs_key_ipv6(ovs_key_proto):
1135        fields = (
1136            ("src", "!16s"),
1137            ("dst", "!16s"),
1138            ("label", "!I"),
1139            ("proto", "B"),
1140            ("tclass", "B"),
1141            ("hlimit", "B"),
1142            ("frag", "B"),
1143        )
1144
1145        fields_map = (
1146            (
1147                "src",
1148                "src",
1149                lambda x: str(ipaddress.IPv6Address(x)),
1150                lambda x: ipaddress.IPv6Address(x).packed if x else 0,
1151                convert_ipv6,
1152            ),
1153            (
1154                "dst",
1155                "dst",
1156                lambda x: str(ipaddress.IPv6Address(x)),
1157                lambda x: ipaddress.IPv6Address(x).packed if x else 0,
1158                convert_ipv6,
1159            ),
1160            ("label", "label", "%d", lambda x: int(x) if x else 0),
1161            ("proto", "proto", "%d", lambda x: int(x) if x else 0),
1162            ("tclass", "tclass", "%d", lambda x: int(x) if x else 0),
1163            ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0),
1164            ("frag", "frag", "%d", lambda x: int(x) if x else 0),
1165        )
1166
1167        def __init__(
1168            self,
1169            data=None,
1170            offset=None,
1171            parent=None,
1172            length=None,
1173            init=None,
1174        ):
1175            ovskey.ovs_key_proto.__init__(
1176                self,
1177                "ipv6",
1178                data=data,
1179                offset=offset,
1180                parent=parent,
1181                length=length,
1182                init=init,
1183            )
1184
1185    class ovs_key_tcp(ovs_key_proto):
1186        def __init__(
1187            self,
1188            data=None,
1189            offset=None,
1190            parent=None,
1191            length=None,
1192            init=None,
1193        ):
1194            ovskey.ovs_key_proto.__init__(
1195                self,
1196                "tcp",
1197                data=data,
1198                offset=offset,
1199                parent=parent,
1200                length=length,
1201                init=init,
1202            )
1203
1204    class ovs_key_udp(ovs_key_proto):
1205        def __init__(
1206            self,
1207            data=None,
1208            offset=None,
1209            parent=None,
1210            length=None,
1211            init=None,
1212        ):
1213            ovskey.ovs_key_proto.__init__(
1214                self,
1215                "udp",
1216                data=data,
1217                offset=offset,
1218                parent=parent,
1219                length=length,
1220                init=init,
1221            )
1222
1223    class ovs_key_sctp(ovs_key_proto):
1224        def __init__(
1225            self,
1226            data=None,
1227            offset=None,
1228            parent=None,
1229            length=None,
1230            init=None,
1231        ):
1232            ovskey.ovs_key_proto.__init__(
1233                self,
1234                "sctp",
1235                data=data,
1236                offset=offset,
1237                parent=parent,
1238                length=length,
1239                init=init,
1240            )
1241
1242    class ovs_key_icmp(ovs_key_proto):
1243        fields = (
1244            ("type", "B"),
1245            ("code", "B"),
1246        )
1247
1248        fields_map = (
1249            ("type", "type", "%d", lambda x: int(x) if x else 0),
1250            ("code", "code", "%d", lambda x: int(x) if x else 0),
1251        )
1252
1253        def __init__(
1254            self,
1255            data=None,
1256            offset=None,
1257            parent=None,
1258            length=None,
1259            init=None,
1260        ):
1261            ovskey.ovs_key_proto.__init__(
1262                self,
1263                "icmp",
1264                data=data,
1265                offset=offset,
1266                parent=parent,
1267                length=length,
1268                init=init,
1269            )
1270
1271    class ovs_key_icmpv6(ovs_key_icmp):
1272        def __init__(
1273            self,
1274            data=None,
1275            offset=None,
1276            parent=None,
1277            length=None,
1278            init=None,
1279        ):
1280            ovskey.ovs_key_proto.__init__(
1281                self,
1282                "icmpv6",
1283                data=data,
1284                offset=offset,
1285                parent=parent,
1286                length=length,
1287                init=init,
1288            )
1289
1290    class ovs_key_arp(ovs_key_proto):
1291        fields = (
1292            ("sip", "!I"),
1293            ("tip", "!I"),
1294            ("op", "!H"),
1295            ("sha", "!6s"),
1296            ("tha", "!6s"),
1297            ("pad", "xx"),
1298        )
1299
1300        fields_map = (
1301            (
1302                "sip",
1303                "sip",
1304                lambda x: str(ipaddress.IPv4Address(x)),
1305                int,
1306                convert_ipv4,
1307            ),
1308            (
1309                "tip",
1310                "tip",
1311                lambda x: str(ipaddress.IPv4Address(x)),
1312                int,
1313                convert_ipv4,
1314            ),
1315            ("op", "op", "%d", lambda x: int(x) if x else 0),
1316            (
1317                "sha",
1318                "sha",
1319                macstr,
1320                lambda x: int.from_bytes(x, "big"),
1321                convert_mac,
1322            ),
1323            (
1324                "tha",
1325                "tha",
1326                macstr,
1327                lambda x: int.from_bytes(x, "big"),
1328                convert_mac,
1329            ),
1330        )
1331
1332        def __init__(
1333            self,
1334            data=None,
1335            offset=None,
1336            parent=None,
1337            length=None,
1338            init=None,
1339        ):
1340            ovskey.ovs_key_proto.__init__(
1341                self,
1342                "arp",
1343                data=data,
1344                offset=offset,
1345                parent=parent,
1346                length=length,
1347                init=init,
1348            )
1349
1350    class ovs_key_nd(ovs_key_proto):
1351        fields = (
1352            ("target", "!16s"),
1353            ("sll", "!6s"),
1354            ("tll", "!6s"),
1355        )
1356
1357        fields_map = (
1358            (
1359                "target",
1360                "target",
1361                lambda x: str(ipaddress.IPv6Address(x)),
1362                convert_ipv6,
1363            ),
1364            ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")),
1365            ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")),
1366        )
1367
1368        def __init__(
1369            self,
1370            data=None,
1371            offset=None,
1372            parent=None,
1373            length=None,
1374            init=None,
1375        ):
1376            ovskey.ovs_key_proto.__init__(
1377                self,
1378                "nd",
1379                data=data,
1380                offset=offset,
1381                parent=parent,
1382                length=length,
1383                init=init,
1384            )
1385
1386    class ovs_key_ct_tuple_ipv4(ovs_key_proto):
1387        fields = (
1388            ("src", "!I"),
1389            ("dst", "!I"),
1390            ("tp_src", "!H"),
1391            ("tp_dst", "!H"),
1392            ("proto", "B"),
1393        )
1394
1395        fields_map = (
1396            (
1397                "src",
1398                "src",
1399                lambda x: str(ipaddress.IPv4Address(x)),
1400                int,
1401                convert_ipv4,
1402            ),
1403            (
1404                "dst",
1405                "dst",
1406                lambda x: str(ipaddress.IPv4Address(x)),
1407                int,
1408                convert_ipv4,
1409            ),
1410            ("tp_src", "tp_src", "%d", int),
1411            ("tp_dst", "tp_dst", "%d", int),
1412            ("proto", "proto", "%d", int),
1413        )
1414
1415        def __init__(
1416            self,
1417            data=None,
1418            offset=None,
1419            parent=None,
1420            length=None,
1421            init=None,
1422        ):
1423            ovskey.ovs_key_proto.__init__(
1424                self,
1425                "ct_tuple4",
1426                data=data,
1427                offset=offset,
1428                parent=parent,
1429                length=length,
1430                init=init,
1431            )
1432
1433    class ovs_key_ct_tuple_ipv6(nla):
1434        fields = (
1435            ("src", "!16s"),
1436            ("dst", "!16s"),
1437            ("tp_src", "!H"),
1438            ("tp_dst", "!H"),
1439            ("proto", "B"),
1440        )
1441
1442        fields_map = (
1443            (
1444                "src",
1445                "src",
1446                lambda x: str(ipaddress.IPv6Address(x)),
1447                convert_ipv6,
1448            ),
1449            (
1450                "dst",
1451                "dst",
1452                lambda x: str(ipaddress.IPv6Address(x)),
1453                convert_ipv6,
1454            ),
1455            ("tp_src", "tp_src", "%d", int),
1456            ("tp_dst", "tp_dst", "%d", int),
1457            ("proto", "proto", "%d", int),
1458        )
1459
1460        def __init__(
1461            self,
1462            data=None,
1463            offset=None,
1464            parent=None,
1465            length=None,
1466            init=None,
1467        ):
1468            ovskey.ovs_key_proto.__init__(
1469                self,
1470                "ct_tuple6",
1471                data=data,
1472                offset=offset,
1473                parent=parent,
1474                length=length,
1475                init=init,
1476            )
1477
1478    class ovs_key_tunnel(nla):
1479        nla_flags = NLA_F_NESTED
1480
1481        nla_map = (
1482            ("OVS_TUNNEL_KEY_ATTR_ID", "be64"),
1483            ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"),
1484            ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"),
1485            ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"),
1486            ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"),
1487            ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"),
1488            ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"),
1489            ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"),
1490            ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"),
1491            ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"),
1492            ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"),
1493            ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"),
1494            ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"),
1495            ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"),
1496            ("OVS_TUNNEL_KEY_ATTR_PAD", "none"),
1497            ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"),
1498            ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"),
1499        )
1500
1501        def parse(self, flowstr, mask=None):
1502            if not flowstr.startswith("tunnel("):
1503                return None, None
1504
1505            k = ovskey.ovs_key_tunnel()
1506            if mask is not None:
1507                mask = ovskey.ovs_key_tunnel()
1508
1509            flowstr = flowstr[len("tunnel("):]
1510
1511            v6_address = None
1512
1513            fields = [
1514                ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID",
1515                 0xffffffffffffffff, None, None),
1516
1517                ("src=", r"([0-9a-fA-F\.]+)", str,
1518                 "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0",
1519                 False),
1520                ("dst=", r"([0-9a-fA-F\.]+)", str,
1521                 "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0",
1522                 False),
1523
1524                ("ipv6_src=", r"([0-9a-fA-F:]+)", str,
1525                 "OVS_TUNNEL_KEY_ATTR_IPV6_SRC",
1526                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),
1527                ("ipv6_dst=", r"([0-9a-fA-F:]+)", str,
1528                 "OVS_TUNNEL_KEY_ATTR_IPV6_DST",
1529                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),
1530
1531                ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0,
1532                 None),
1533                ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0,
1534                 None),
1535
1536                ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC",
1537                 65535, 0, None),
1538                ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST",
1539                 65535, 0, None),
1540            ]
1541
1542            forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"]
1543
1544            for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields:
1545                flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False)
1546                if not attr_name:
1547                    raise Exception("Bad list value in tunnel fields")
1548
1549                if value is None and attr_name in forced_include:
1550                    value = default_val
1551                    mask_val = default_val
1552
1553                if value is not None:
1554                    if v46_flag is not None:
1555                        if v6_address is None:
1556                            v6_address = v46_flag
1557                        if v46_flag != v6_address:
1558                            raise ValueError("Cannot mix v6 and v4 addresses")
1559                    k["attrs"].append([attr_name, value])
1560                    if mask is not None:
1561                        mask["attrs"].append([attr_name, mask_val])
1562                else:
1563                    if v46_flag is not None:
1564                        if v6_address is None or v46_flag != v6_address:
1565                            continue
1566                    if mask is not None:
1567                        mask["attrs"].append([attr_name, default_val])
1568
1569            if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID":
1570                raise ValueError("Needs a tunid set")
1571
1572            if flowstr.startswith("flags("):
1573                flowstr = flowstr[len("flags("):]
1574                flagspos = flowstr.find(")")
1575                flags = flowstr[:flagspos]
1576                flowstr = flowstr[flagspos + 1:]
1577
1578                flag_attrs = {
1579                    "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT",
1580                    "csum": "OVS_TUNNEL_KEY_ATTR_CSUM",
1581                    "oam": "OVS_TUNNEL_KEY_ATTR_OAM"
1582                }
1583
1584                for flag in flags.split("|"):
1585                    if flag in flag_attrs:
1586                        k["attrs"].append([flag_attrs[flag], True])
1587                        if mask is not None:
1588                            mask["attrs"].append([flag_attrs[flag], True])
1589
1590            flowstr = flowstr[strspn(flowstr, ", ") :]
1591            return flowstr, k, mask
1592
1593        def dpstr(self, mask=None, more=False):
1594            print_str = "tunnel("
1595
1596            flagsattrs = []
1597            for k in self["attrs"]:
1598                noprint = False
1599                if k[0] == "OVS_TUNNEL_KEY_ATTR_ID":
1600                    print_str += "tun_id=%d" % k[1]
1601                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC":
1602                    print_str += "src=%s" % k[1]
1603                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST":
1604                    print_str += "dst=%s" % k[1]
1605                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC":
1606                    print_str += "ipv6_src=%s" % k[1]
1607                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST":
1608                    print_str += "ipv6_dst=%s" % k[1]
1609                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS":
1610                    print_str += "tos=%d" % k[1]
1611                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL":
1612                    print_str += "ttl=%d" % k[1]
1613                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC":
1614                    print_str += "tp_src=%d" % k[1]
1615                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST":
1616                    print_str += "tp_dst=%d" % k[1]
1617                elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT":
1618                    noprint = True
1619                    flagsattrs.append("df")
1620                elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM":
1621                    noprint = True
1622                    flagsattrs.append("csum")
1623                elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM":
1624                    noprint = True
1625                    flagsattrs.append("oam")
1626
1627                if not noprint:
1628                    print_str += ","
1629
1630            if len(flagsattrs):
1631                print_str += "flags(" + "|".join(flagsattrs) + ")"
1632            print_str += ")"
1633            return print_str
1634
1635    class ovs_key_mpls(nla):
1636        fields = (("lse", ">I"),)
1637
1638    def parse(self, flowstr, mask=None):
1639        for field in (
1640            ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse),
1641            ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse),
1642            ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse),
1643            ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel),
1644            ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse),
1645            ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state),
1646            ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse),
1647            ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse),
1648            ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse),
1649            (
1650                "OVS_KEY_ATTR_ETHERNET",
1651                "eth",
1652                ovskey.ethaddr,
1653            ),
1654            (
1655                "OVS_KEY_ATTR_ETHERTYPE",
1656                "eth_type",
1657                lambda x: intparse(x, "0xffff"),
1658            ),
1659            (
1660                "OVS_KEY_ATTR_IPV4",
1661                "ipv4",
1662                ovskey.ovs_key_ipv4,
1663            ),
1664            (
1665                "OVS_KEY_ATTR_IPV6",
1666                "ipv6",
1667                ovskey.ovs_key_ipv6,
1668            ),
1669            (
1670                "OVS_KEY_ATTR_ARP",
1671                "arp",
1672                ovskey.ovs_key_arp,
1673            ),
1674            (
1675                "OVS_KEY_ATTR_TCP",
1676                "tcp",
1677                ovskey.ovs_key_tcp,
1678            ),
1679            (
1680                "OVS_KEY_ATTR_UDP",
1681                "udp",
1682                ovskey.ovs_key_udp,
1683            ),
1684            (
1685                "OVS_KEY_ATTR_ICMP",
1686                "icmp",
1687                ovskey.ovs_key_icmp,
1688            ),
1689            (
1690                "OVS_KEY_ATTR_TCP_FLAGS",
1691                "tcp_flags",
1692                lambda x: parse_flags(x, None),
1693            ),
1694        ):
1695            fld = field[1] + "("
1696            if not flowstr.startswith(fld):
1697                continue
1698
1699            if not isinstance(field[2], types.FunctionType):
1700                nk = field[2]()
1701                flowstr, k, m = nk.parse(flowstr, field[2])
1702            else:
1703                flowstr = flowstr[len(fld) :]
1704                flowstr, k, m = field[2](flowstr)
1705
1706            if m and mask is not None:
1707                mask["attrs"].append([field[0], m])
1708            self["attrs"].append([field[0], k])
1709
1710            flowstr = flowstr[strspn(flowstr, "), ") :]
1711
1712        return flowstr
1713
1714    def dpstr(self, mask=None, more=False):
1715        print_str = ""
1716
1717        for field in (
1718            (
1719                "OVS_KEY_ATTR_PRIORITY",
1720                "skb_priority",
1721                "%d",
1722                lambda x: False,
1723                True,
1724            ),
1725            (
1726                "OVS_KEY_ATTR_SKB_MARK",
1727                "skb_mark",
1728                "%d",
1729                lambda x: False,
1730                True,
1731            ),
1732            (
1733                "OVS_KEY_ATTR_RECIRC_ID",
1734                "recirc_id",
1735                "0x%08X",
1736                lambda x: False,
1737                True,
1738            ),
1739            (
1740                "OVS_KEY_ATTR_DP_HASH",
1741                "dp_hash",
1742                "0x%08X",
1743                lambda x: False,
1744                True,
1745            ),
1746            (
1747                "OVS_KEY_ATTR_TUNNEL",
1748                "tunnel",
1749                None,
1750                False,
1751                False,
1752            ),
1753            (
1754                "OVS_KEY_ATTR_CT_STATE",
1755                "ct_state",
1756                "0x%04x",
1757                lambda x: False,
1758                True,
1759            ),
1760            (
1761                "OVS_KEY_ATTR_CT_ZONE",
1762                "ct_zone",
1763                "0x%04x",
1764                lambda x: False,
1765                True,
1766            ),
1767            (
1768                "OVS_KEY_ATTR_CT_MARK",
1769                "ct_mark",
1770                "0x%08x",
1771                lambda x: False,
1772                True,
1773            ),
1774            (
1775                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4",
1776                None,
1777                None,
1778                False,
1779                False,
1780            ),
1781            (
1782                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6",
1783                None,
1784                None,
1785                False,
1786                False,
1787            ),
1788            (
1789                "OVS_KEY_ATTR_IN_PORT",
1790                "in_port",
1791                "%d",
1792                lambda x: True,
1793                True,
1794            ),
1795            ("OVS_KEY_ATTR_ETHERNET", None, None, False, False),
1796            (
1797                "OVS_KEY_ATTR_ETHERTYPE",
1798                "eth_type",
1799                "0x%04x",
1800                lambda x: int(x) == 0xFFFF,
1801                True,
1802            ),
1803            ("OVS_KEY_ATTR_IPV4", None, None, False, False),
1804            ("OVS_KEY_ATTR_IPV6", None, None, False, False),
1805            ("OVS_KEY_ATTR_ARP", None, None, False, False),
1806            ("OVS_KEY_ATTR_TCP", None, None, False, False),
1807            (
1808                "OVS_KEY_ATTR_TCP_FLAGS",
1809                "tcp_flags",
1810                "0x%04x",
1811                lambda x: False,
1812                True,
1813            ),
1814            ("OVS_KEY_ATTR_UDP", None, None, False, False),
1815            ("OVS_KEY_ATTR_SCTP", None, None, False, False),
1816            ("OVS_KEY_ATTR_ICMP", None, None, False, False),
1817            ("OVS_KEY_ATTR_ICMPV6", None, None, False, False),
1818            ("OVS_KEY_ATTR_ND", None, None, False, False),
1819        ):
1820            v = self.get_attr(field[0])
1821            if v is not None:
1822                m = None if mask is None else mask.get_attr(field[0])
1823                if field[4] is False:
1824                    print_str += v.dpstr(m, more)
1825                    print_str += ","
1826                else:
1827                    if m is None or field[3](m):
1828                        print_str += field[1] + "("
1829                        print_str += field[2] % v
1830                        print_str += "),"
1831                    elif more or m != 0:
1832                        print_str += field[1] + "("
1833                        print_str += (field[2] % v) + "/" + (field[2] % m)
1834                        print_str += "),"
1835
1836        return print_str
1837
1838
1839class OvsPacket(GenericNetlinkSocket):
1840    OVS_PACKET_CMD_MISS = 1  # Flow table miss
1841    OVS_PACKET_CMD_ACTION = 2  # USERSPACE action
1842    OVS_PACKET_CMD_EXECUTE = 3  # Apply actions to packet
1843
1844    class ovs_packet_msg(ovs_dp_msg):
1845        nla_map = (
1846            ("OVS_PACKET_ATTR_UNSPEC", "none"),
1847            ("OVS_PACKET_ATTR_PACKET", "array(uint8)"),
1848            ("OVS_PACKET_ATTR_KEY", "ovskey"),
1849            ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"),
1850            ("OVS_PACKET_ATTR_USERDATA", "none"),
1851            ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"),
1852            ("OVS_PACKET_ATTR_UNUSED1", "none"),
1853            ("OVS_PACKET_ATTR_UNUSED2", "none"),
1854            ("OVS_PACKET_ATTR_PROBE", "none"),
1855            ("OVS_PACKET_ATTR_MRU", "uint16"),
1856            ("OVS_PACKET_ATTR_LEN", "uint32"),
1857            ("OVS_PACKET_ATTR_HASH", "uint64"),
1858        )
1859
1860    def __init__(self):
1861        GenericNetlinkSocket.__init__(self)
1862        self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg)
1863
1864    def upcall_handler(self, up=None):
1865        print("listening on upcall packet handler:", self.epid)
1866        while True:
1867            try:
1868                msgs = self.get()
1869                for msg in msgs:
1870                    if not up:
1871                        continue
1872                    if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS:
1873                        up.miss(msg)
1874                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION:
1875                        up.action(msg)
1876                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE:
1877                        up.execute(msg)
1878                    else:
1879                        print("Unknown cmd: %d" % msg["cmd"])
1880            except NetlinkError as ne:
1881                raise ne
1882
1883
1884class OvsDatapath(GenericNetlinkSocket):
1885    OVS_DP_F_VPORT_PIDS = 1 << 1
1886    OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
1887
1888    class dp_cmd_msg(ovs_dp_msg):
1889        """
1890        Message class that will be used to communicate with the kernel module.
1891        """
1892
1893        nla_map = (
1894            ("OVS_DP_ATTR_UNSPEC", "none"),
1895            ("OVS_DP_ATTR_NAME", "asciiz"),
1896            ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"),
1897            ("OVS_DP_ATTR_STATS", "dpstats"),
1898            ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"),
1899            ("OVS_DP_ATTR_USER_FEATURES", "uint32"),
1900            ("OVS_DP_ATTR_PAD", "none"),
1901            ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"),
1902            ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"),
1903        )
1904
1905        class dpstats(nla):
1906            fields = (
1907                ("hit", "=Q"),
1908                ("missed", "=Q"),
1909                ("lost", "=Q"),
1910                ("flows", "=Q"),
1911            )
1912
1913        class megaflowstats(nla):
1914            fields = (
1915                ("mask_hit", "=Q"),
1916                ("masks", "=I"),
1917                ("padding", "=I"),
1918                ("cache_hits", "=Q"),
1919                ("pad1", "=Q"),
1920            )
1921
1922    def __init__(self):
1923        GenericNetlinkSocket.__init__(self)
1924        self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg)
1925
1926    def info(self, dpname, ifindex=0):
1927        msg = OvsDatapath.dp_cmd_msg()
1928        msg["cmd"] = OVS_DP_CMD_GET
1929        msg["version"] = OVS_DATAPATH_VERSION
1930        msg["reserved"] = 0
1931        msg["dpifindex"] = ifindex
1932        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1933
1934        try:
1935            reply = self.nlm_request(
1936                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST
1937            )
1938            reply = reply[0]
1939        except NetlinkError as ne:
1940            if ne.code == errno.ENODEV:
1941                reply = None
1942            else:
1943                raise ne
1944
1945        return reply
1946
1947    def create(
1948        self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket()
1949    ):
1950        msg = OvsDatapath.dp_cmd_msg()
1951        msg["cmd"] = OVS_DP_CMD_NEW
1952        if versionStr is None:
1953            msg["version"] = OVS_DATAPATH_VERSION
1954        else:
1955            msg["version"] = int(versionStr.split(":")[0], 0)
1956        msg["reserved"] = 0
1957        msg["dpifindex"] = 0
1958        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1959
1960        dpfeatures = 0
1961        if versionStr is not None and versionStr.find(":") != -1:
1962            dpfeatures = int(versionStr.split(":")[1], 0)
1963        else:
1964            if versionStr is None or versionStr.find(":") == -1:
1965                dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU
1966                dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS
1967
1968            nproc = multiprocessing.cpu_count()
1969            procarray = []
1970            for i in range(1, nproc):
1971                procarray += [int(p.epid)]
1972            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray])
1973        msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures])
1974        if not shouldUpcall:
1975            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]])
1976
1977        try:
1978            reply = self.nlm_request(
1979                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1980            )
1981            reply = reply[0]
1982        except NetlinkError as ne:
1983            if ne.code == errno.EEXIST:
1984                reply = None
1985            else:
1986                raise ne
1987
1988        return reply
1989
1990    def destroy(self, dpname):
1991        msg = OvsDatapath.dp_cmd_msg()
1992        msg["cmd"] = OVS_DP_CMD_DEL
1993        msg["version"] = OVS_DATAPATH_VERSION
1994        msg["reserved"] = 0
1995        msg["dpifindex"] = 0
1996        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1997
1998        try:
1999            reply = self.nlm_request(
2000                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
2001            )
2002            reply = reply[0]
2003        except NetlinkError as ne:
2004            if ne.code == errno.ENODEV:
2005                reply = None
2006            else:
2007                raise ne
2008
2009        return reply
2010
2011
2012class OvsVport(GenericNetlinkSocket):
2013    OVS_VPORT_TYPE_NETDEV = 1
2014    OVS_VPORT_TYPE_INTERNAL = 2
2015    OVS_VPORT_TYPE_GRE = 3
2016    OVS_VPORT_TYPE_VXLAN = 4
2017    OVS_VPORT_TYPE_GENEVE = 5
2018
2019    class ovs_vport_msg(ovs_dp_msg):
2020        nla_map = (
2021            ("OVS_VPORT_ATTR_UNSPEC", "none"),
2022            ("OVS_VPORT_ATTR_PORT_NO", "uint32"),
2023            ("OVS_VPORT_ATTR_TYPE", "uint32"),
2024            ("OVS_VPORT_ATTR_NAME", "asciiz"),
2025            ("OVS_VPORT_ATTR_OPTIONS", "vportopts"),
2026            ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"),
2027            ("OVS_VPORT_ATTR_STATS", "vportstats"),
2028            ("OVS_VPORT_ATTR_PAD", "none"),
2029            ("OVS_VPORT_ATTR_IFINDEX", "uint32"),
2030            ("OVS_VPORT_ATTR_NETNSID", "uint32"),
2031        )
2032
2033        class vportopts(nla):
2034            nla_map = (
2035                ("OVS_TUNNEL_ATTR_UNSPEC", "none"),
2036                ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"),
2037                ("OVS_TUNNEL_ATTR_EXTENSION", "none"),
2038            )
2039
2040        class vportstats(nla):
2041            fields = (
2042                ("rx_packets", "=Q"),
2043                ("tx_packets", "=Q"),
2044                ("rx_bytes", "=Q"),
2045                ("tx_bytes", "=Q"),
2046                ("rx_errors", "=Q"),
2047                ("tx_errors", "=Q"),
2048                ("rx_dropped", "=Q"),
2049                ("tx_dropped", "=Q"),
2050            )
2051
2052    def type_to_str(vport_type):
2053        if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:
2054            return "netdev"
2055        elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
2056            return "internal"
2057        elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE:
2058            return "gre"
2059        elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN:
2060            return "vxlan"
2061        elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE:
2062            return "geneve"
2063        raise ValueError("Unknown vport type:%d" % vport_type)
2064
2065    def str_to_type(vport_type):
2066        if vport_type == "netdev":
2067            return OvsVport.OVS_VPORT_TYPE_NETDEV
2068        elif vport_type == "internal":
2069            return OvsVport.OVS_VPORT_TYPE_INTERNAL
2070        elif vport_type == "gre":
2071            return OvsVport.OVS_VPORT_TYPE_GRE
2072        elif vport_type == "vxlan":
2073            return OvsVport.OVS_VPORT_TYPE_VXLAN
2074        elif vport_type == "geneve":
2075            return OvsVport.OVS_VPORT_TYPE_GENEVE
2076        raise ValueError("Unknown vport type: '%s'" % vport_type)
2077
2078    def __init__(self, packet=OvsPacket()):
2079        GenericNetlinkSocket.__init__(self)
2080        self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg)
2081        self.upcall_packet = packet
2082
2083    def info(self, vport_name, dpifindex=0, portno=None):
2084        msg = OvsVport.ovs_vport_msg()
2085
2086        msg["cmd"] = OVS_VPORT_CMD_GET
2087        msg["version"] = OVS_DATAPATH_VERSION
2088        msg["reserved"] = 0
2089        msg["dpifindex"] = dpifindex
2090
2091        if portno is None:
2092            msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name])
2093        else:
2094            msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno])
2095
2096        try:
2097            reply = self.nlm_request(
2098                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST
2099            )
2100            reply = reply[0]
2101        except NetlinkError as ne:
2102            if ne.code == errno.ENODEV:
2103                reply = None
2104            else:
2105                raise ne
2106        return reply
2107
2108    def attach(self, dpindex, vport_ifname, ptype, dport, lwt):
2109        msg = OvsVport.ovs_vport_msg()
2110
2111        msg["cmd"] = OVS_VPORT_CMD_NEW
2112        msg["version"] = OVS_DATAPATH_VERSION
2113        msg["reserved"] = 0
2114        msg["dpifindex"] = dpindex
2115        port_type = OvsVport.str_to_type(ptype)
2116
2117        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
2118        msg["attrs"].append(
2119            ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]]
2120        )
2121
2122        TUNNEL_DEFAULTS = [("geneve", 6081),
2123                           ("gre", 0),
2124                           ("vxlan", 4789)]
2125
2126        for tnl in TUNNEL_DEFAULTS:
2127            if ptype == tnl[0]:
2128                if not dport:
2129                    dport = tnl[1]
2130
2131                if not lwt:
2132                    if tnl[0] == "gre":
2133                        # GRE tunnels have no options.
2134                        break
2135
2136                    vportopt = OvsVport.ovs_vport_msg.vportopts()
2137                    vportopt["attrs"].append(
2138                        ["OVS_TUNNEL_ATTR_DST_PORT", dport]
2139                    )
2140                    msg["attrs"].append(
2141                        ["OVS_VPORT_ATTR_OPTIONS", vportopt]
2142                    )
2143                else:
2144                    port_type = OvsVport.OVS_VPORT_TYPE_NETDEV
2145                    ipr = pyroute2.iproute.IPRoute()
2146
2147                    if tnl[0] == "geneve":
2148                        ipr.link("add", ifname=vport_ifname, kind=tnl[0],
2149                                 geneve_port=dport,
2150                                 geneve_collect_metadata=True,
2151                                 geneve_udp_zero_csum6_rx=1)
2152                    elif tnl[0] == "gre":
2153                        ipr.link("add", ifname=vport_ifname, kind="gretap",
2154                                 gre_collect_metadata=True)
2155                    elif tnl[0] == "vxlan":
2156                        ipr.link("add", ifname=vport_ifname, kind=tnl[0],
2157                                 vxlan_learning=0, vxlan_collect_metadata=1,
2158                                 vxlan_udp_zero_csum6_rx=1, vxlan_port=dport)
2159                break
2160        msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type])
2161
2162        try:
2163            reply = self.nlm_request(
2164                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
2165            )
2166            reply = reply[0]
2167        except NetlinkError as ne:
2168            if ne.code == errno.EEXIST:
2169                reply = None
2170            else:
2171                raise ne
2172        return reply
2173
2174    def reset_upcall(self, dpindex, vport_ifname, p=None):
2175        msg = OvsVport.ovs_vport_msg()
2176
2177        msg["cmd"] = OVS_VPORT_CMD_SET
2178        msg["version"] = OVS_DATAPATH_VERSION
2179        msg["reserved"] = 0
2180        msg["dpifindex"] = dpindex
2181        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
2182
2183        if p == None:
2184            p = self.upcall_packet
2185        else:
2186            self.upcall_packet = p
2187
2188        msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]])
2189
2190        try:
2191            reply = self.nlm_request(
2192                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
2193            )
2194            reply = reply[0]
2195        except NetlinkError as ne:
2196            raise ne
2197        return reply
2198
2199    def detach(self, dpindex, vport_ifname):
2200        msg = OvsVport.ovs_vport_msg()
2201
2202        msg["cmd"] = OVS_VPORT_CMD_DEL
2203        msg["version"] = OVS_DATAPATH_VERSION
2204        msg["reserved"] = 0
2205        msg["dpifindex"] = dpindex
2206        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
2207
2208        try:
2209            reply = self.nlm_request(
2210                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
2211            )
2212            reply = reply[0]
2213        except NetlinkError as ne:
2214            if ne.code == errno.ENODEV:
2215                reply = None
2216            else:
2217                raise ne
2218        return reply
2219
2220    def upcall_handler(self, handler=None):
2221        self.upcall_packet.upcall_handler(handler)
2222
2223
2224class OvsFlow(GenericNetlinkSocket):
2225    class ovs_flow_msg(ovs_dp_msg):
2226        nla_map = (
2227            ("OVS_FLOW_ATTR_UNSPEC", "none"),
2228            ("OVS_FLOW_ATTR_KEY", "ovskey"),
2229            ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"),
2230            ("OVS_FLOW_ATTR_STATS", "flowstats"),
2231            ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"),
2232            ("OVS_FLOW_ATTR_USED", "uint64"),
2233            ("OVS_FLOW_ATTR_CLEAR", "none"),
2234            ("OVS_FLOW_ATTR_MASK", "ovskey"),
2235            ("OVS_FLOW_ATTR_PROBE", "none"),
2236            ("OVS_FLOW_ATTR_UFID", "array(uint32)"),
2237            ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"),
2238        )
2239
2240        class flowstats(nla):
2241            fields = (
2242                ("packets", "=Q"),
2243                ("bytes", "=Q"),
2244            )
2245
2246        def dpstr(self, more=False):
2247            ufid = self.get_attr("OVS_FLOW_ATTR_UFID")
2248            ufid_str = ""
2249            if ufid is not None:
2250                ufid_str = (
2251                    "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(
2252                        ufid[0],
2253                        ufid[1] >> 16,
2254                        ufid[1] & 0xFFFF,
2255                        ufid[2] >> 16,
2256                        ufid[2] & 0,
2257                        ufid[3],
2258                    )
2259                )
2260
2261            key_field = self.get_attr("OVS_FLOW_ATTR_KEY")
2262            keymsg = None
2263            if key_field is not None:
2264                keymsg = key_field
2265
2266            mask_field = self.get_attr("OVS_FLOW_ATTR_MASK")
2267            maskmsg = None
2268            if mask_field is not None:
2269                maskmsg = mask_field
2270
2271            acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS")
2272            actsmsg = None
2273            if acts_field is not None:
2274                actsmsg = acts_field
2275
2276            print_str = ""
2277
2278            if more:
2279                print_str += ufid_str + ","
2280
2281            if keymsg is not None:
2282                print_str += keymsg.dpstr(maskmsg, more)
2283
2284            stats = self.get_attr("OVS_FLOW_ATTR_STATS")
2285            if stats is None:
2286                print_str += " packets:0, bytes:0,"
2287            else:
2288                print_str += " packets:%d, bytes:%d," % (
2289                    stats["packets"],
2290                    stats["bytes"],
2291                )
2292
2293            used = self.get_attr("OVS_FLOW_ATTR_USED")
2294            print_str += " used:"
2295            if used is None:
2296                print_str += "never,"
2297            else:
2298                used_time = int(used)
2299                cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC)
2300                used_time = (cur_time_sec * 1000) - used_time
2301                print_str += "{}s,".format(used_time / 1000)
2302
2303            print_str += " actions:"
2304            if (
2305                actsmsg is None
2306                or "attrs" not in actsmsg
2307                or len(actsmsg["attrs"]) == 0
2308            ):
2309                print_str += "drop"
2310            else:
2311                print_str += actsmsg.dpstr(more)
2312
2313            return print_str
2314
2315        def parse(self, flowstr, actstr, dpidx=0):
2316            OVS_UFID_F_OMIT_KEY = 1 << 0
2317            OVS_UFID_F_OMIT_MASK = 1 << 1
2318            OVS_UFID_F_OMIT_ACTIONS = 1 << 2
2319
2320            self["cmd"] = 0
2321            self["version"] = 0
2322            self["reserved"] = 0
2323            self["dpifindex"] = 0
2324
2325            if flowstr.startswith("ufid:"):
2326                count = 5
2327                while flowstr[count] != ",":
2328                    count += 1
2329                ufidstr = flowstr[5:count]
2330                flowstr = flowstr[count + 1 :]
2331            else:
2332                ufidstr = str(uuid.uuid4())
2333            uuidRawObj = uuid.UUID(ufidstr).fields
2334
2335            self["attrs"].append(
2336                [
2337                    "OVS_FLOW_ATTR_UFID",
2338                    [
2339                        uuidRawObj[0],
2340                        uuidRawObj[1] << 16 | uuidRawObj[2],
2341                        uuidRawObj[3] << 24
2342                        | uuidRawObj[4] << 16
2343                        | uuidRawObj[5] & (0xFF << 32) >> 32,
2344                        uuidRawObj[5] & (0xFFFFFFFF),
2345                    ],
2346                ]
2347            )
2348            self["attrs"].append(
2349                [
2350                    "OVS_FLOW_ATTR_UFID_FLAGS",
2351                    int(
2352                        OVS_UFID_F_OMIT_KEY
2353                        | OVS_UFID_F_OMIT_MASK
2354                        | OVS_UFID_F_OMIT_ACTIONS
2355                    ),
2356                ]
2357            )
2358
2359            k = ovskey()
2360            m = ovskey()
2361            k.parse(flowstr, m)
2362            self["attrs"].append(["OVS_FLOW_ATTR_KEY", k])
2363            self["attrs"].append(["OVS_FLOW_ATTR_MASK", m])
2364
2365            a = ovsactions()
2366            a.parse(actstr)
2367            self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a])
2368
2369    def __init__(self):
2370        GenericNetlinkSocket.__init__(self)
2371
2372        self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg)
2373
2374    def add_flow(self, dpifindex, flowmsg):
2375        """
2376        Send a new flow message to the kernel.
2377
2378        dpifindex should be a valid datapath obtained by calling
2379        into the OvsDatapath lookup
2380
2381        flowmsg is a flow object obtained by calling a dpparse
2382        """
2383
2384        flowmsg["cmd"] = OVS_FLOW_CMD_NEW
2385        flowmsg["version"] = OVS_DATAPATH_VERSION
2386        flowmsg["reserved"] = 0
2387        flowmsg["dpifindex"] = dpifindex
2388
2389        try:
2390            reply = self.nlm_request(
2391                flowmsg,
2392                msg_type=self.prid,
2393                msg_flags=NLM_F_REQUEST | NLM_F_ACK,
2394            )
2395            reply = reply[0]
2396        except NetlinkError as ne:
2397            print(flowmsg)
2398            raise ne
2399        return reply
2400
2401    def del_flows(self, dpifindex):
2402        """
2403        Send a del message to the kernel that will drop all flows.
2404
2405        dpifindex should be a valid datapath obtained by calling
2406        into the OvsDatapath lookup
2407        """
2408
2409        flowmsg = OvsFlow.ovs_flow_msg()
2410        flowmsg["cmd"] = OVS_FLOW_CMD_DEL
2411        flowmsg["version"] = OVS_DATAPATH_VERSION
2412        flowmsg["reserved"] = 0
2413        flowmsg["dpifindex"] = dpifindex
2414
2415        try:
2416            reply = self.nlm_request(
2417                flowmsg,
2418                msg_type=self.prid,
2419                msg_flags=NLM_F_REQUEST | NLM_F_ACK,
2420            )
2421            reply = reply[0]
2422        except NetlinkError as ne:
2423            print(flowmsg)
2424            raise ne
2425        return reply
2426
2427    def dump(self, dpifindex, flowspec=None):
2428        """
2429        Returns a list of messages containing flows.
2430
2431        dpifindex should be a valid datapath obtained by calling
2432        into the OvsDatapath lookup
2433
2434        flowpsec is a string which represents a flow in the dpctl
2435        format.
2436        """
2437        msg = OvsFlow.ovs_flow_msg()
2438
2439        msg["cmd"] = OVS_FLOW_CMD_GET
2440        msg["version"] = OVS_DATAPATH_VERSION
2441        msg["reserved"] = 0
2442        msg["dpifindex"] = dpifindex
2443
2444        msg_flags = NLM_F_REQUEST | NLM_F_ACK
2445        if flowspec is None:
2446            msg_flags |= NLM_F_DUMP
2447        rep = None
2448
2449        try:
2450            rep = self.nlm_request(
2451                msg,
2452                msg_type=self.prid,
2453                msg_flags=msg_flags,
2454            )
2455        except NetlinkError as ne:
2456            raise ne
2457        return rep
2458
2459    def miss(self, packetmsg):
2460        seq = packetmsg["header"]["sequence_number"]
2461        keystr = "(none)"
2462        key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY")
2463        if key_field is not None:
2464            keystr = key_field.dpstr(None, True)
2465
2466        pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET")
2467        pktpres = "yes" if pktdata is not None else "no"
2468
2469        print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True)
2470
2471    def execute(self, packetmsg):
2472        print("userspace execute command", flush=True)
2473
2474    def action(self, packetmsg):
2475        print("userspace action command", flush=True)
2476
2477
2478class psample_sample(genlmsg):
2479    nla_map = (
2480        ("PSAMPLE_ATTR_IIFINDEX", "none"),
2481        ("PSAMPLE_ATTR_OIFINDEX", "none"),
2482        ("PSAMPLE_ATTR_ORIGSIZE", "none"),
2483        ("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"),
2484        ("PSAMPLE_ATTR_GROUP_SEQ", "none"),
2485        ("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"),
2486        ("PSAMPLE_ATTR_DATA", "array(uint8)"),
2487        ("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"),
2488        ("PSAMPLE_ATTR_TUNNEL", "none"),
2489        ("PSAMPLE_ATTR_PAD", "none"),
2490        ("PSAMPLE_ATTR_OUT_TC", "none"),
2491        ("PSAMPLE_ATTR_OUT_TC_OCC", "none"),
2492        ("PSAMPLE_ATTR_LATENCY", "none"),
2493        ("PSAMPLE_ATTR_TIMESTAMP", "none"),
2494        ("PSAMPLE_ATTR_PROTO", "none"),
2495        ("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"),
2496    )
2497
2498    def dpstr(self):
2499        fields = []
2500        data = ""
2501        for (attr, value) in self["attrs"]:
2502            if attr == "PSAMPLE_ATTR_SAMPLE_GROUP":
2503                fields.append("group:%d" % value)
2504            if attr == "PSAMPLE_ATTR_SAMPLE_RATE":
2505                fields.append("rate:%d" % value)
2506            if attr == "PSAMPLE_ATTR_USER_COOKIE":
2507                value = "".join(format(x, "02x") for x in value)
2508                fields.append("cookie:%s" % value)
2509            if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0:
2510                data = "data:%s" % "".join(format(x, "02x") for x in value)
2511
2512        return ("%s %s" % (",".join(fields), data)).strip()
2513
2514
2515class psample_msg(Marshal):
2516    PSAMPLE_CMD_SAMPLE = 0
2517    PSAMPLE_CMD_GET_GROUP = 1
2518    PSAMPLE_CMD_NEW_GROUP = 2
2519    PSAMPLE_CMD_DEL_GROUP = 3
2520    PSAMPLE_CMD_SET_FILTER = 4
2521    msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample}
2522
2523
2524class PsampleEvent(EventSocket):
2525    genl_family = "psample"
2526    mcast_groups = ["packets"]
2527    marshal_class = psample_msg
2528
2529    def read_samples(self):
2530        print("listening for psample events", flush=True)
2531        while True:
2532            try:
2533                for msg in self.get():
2534                    print(msg.dpstr(), flush=True)
2535            except NetlinkError as ne:
2536                raise ne
2537
2538
2539def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
2540    dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
2541    base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
2542    megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")
2543    user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES")
2544    masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE")
2545
2546    print("%s:" % dp_name)
2547    print(
2548        "  lookups: hit:%d missed:%d lost:%d"
2549        % (base_stats["hit"], base_stats["missed"], base_stats["lost"])
2550    )
2551    print("  flows:%d" % base_stats["flows"])
2552    pkts = base_stats["hit"] + base_stats["missed"]
2553    avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0
2554    print(
2555        "  masks: hit:%d total:%d hit/pkt:%f"
2556        % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg)
2557    )
2558    print("  caches:")
2559    print("    masks-cache: size:%d" % masks_cache_size)
2560
2561    if user_features is not None:
2562        print("  features: 0x%X" % user_features)
2563
2564    # port print out
2565    for iface in ndb.interfaces:
2566        rep = vpl.info(iface.ifname, ifindex)
2567        if rep is not None:
2568            opts = ""
2569            vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS")
2570            if vpo:
2571                dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT")
2572                if dpo:
2573                    opts += " tnl-dport:%s" % dpo
2574            print(
2575                "  port %d: %s (%s%s)"
2576                % (
2577                    rep.get_attr("OVS_VPORT_ATTR_PORT_NO"),
2578                    rep.get_attr("OVS_VPORT_ATTR_NAME"),
2579                    OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")),
2580                    opts,
2581                )
2582            )
2583
2584
2585def main(argv):
2586    nlmsg_atoms.ovskey = ovskey
2587    nlmsg_atoms.ovsactions = ovsactions
2588
2589    # version check for pyroute2
2590    prverscheck = pyroute2.__version__.split(".")
2591    if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6:
2592        print("Need to upgrade the python pyroute2 package to >= 0.6.")
2593        sys.exit(1)
2594
2595    parser = argparse.ArgumentParser()
2596    parser.add_argument(
2597        "-v",
2598        "--verbose",
2599        action="count",
2600        help="Increment 'verbose' output counter.",
2601        default=0,
2602    )
2603    subparsers = parser.add_subparsers(dest="subcommand")
2604
2605    showdpcmd = subparsers.add_parser("show")
2606    showdpcmd.add_argument(
2607        "showdp", metavar="N", type=str, nargs="?", help="Datapath Name"
2608    )
2609
2610    adddpcmd = subparsers.add_parser("add-dp")
2611    adddpcmd.add_argument("adddp", help="Datapath Name")
2612    adddpcmd.add_argument(
2613        "-u",
2614        "--upcall",
2615        action="store_true",
2616        help="Leave open a reader for upcalls",
2617    )
2618    adddpcmd.add_argument(
2619        "-V",
2620        "--versioning",
2621        required=False,
2622        help="Specify a custom version / feature string",
2623    )
2624
2625    deldpcmd = subparsers.add_parser("del-dp")
2626    deldpcmd.add_argument("deldp", help="Datapath Name")
2627
2628    addifcmd = subparsers.add_parser("add-if")
2629    addifcmd.add_argument("dpname", help="Datapath Name")
2630    addifcmd.add_argument("addif", help="Interface name for adding")
2631    addifcmd.add_argument(
2632        "-u",
2633        "--upcall",
2634        action="store_true",
2635        help="Leave open a reader for upcalls",
2636    )
2637    addifcmd.add_argument(
2638        "-t",
2639        "--ptype",
2640        type=str,
2641        default="netdev",
2642        choices=["netdev", "internal", "gre", "geneve", "vxlan"],
2643        help="Interface type (default netdev)",
2644    )
2645    addifcmd.add_argument(
2646        "-p",
2647        "--dport",
2648        type=int,
2649        default=0,
2650        help="Destination port (0 for default)"
2651    )
2652    addifcmd.add_argument(
2653        "-l",
2654        "--lwt",
2655        action=argparse.BooleanOptionalAction,
2656        default=True,
2657        help="Use LWT infrastructure instead of vport (default true)."
2658    )
2659    delifcmd = subparsers.add_parser("del-if")
2660    delifcmd.add_argument("dpname", help="Datapath Name")
2661    delifcmd.add_argument("delif", help="Interface name for adding")
2662    delifcmd.add_argument("-d",
2663                          "--dellink",
2664                          type=bool, default=False,
2665                          help="Delete the link as well.")
2666
2667    dumpflcmd = subparsers.add_parser("dump-flows")
2668    dumpflcmd.add_argument("dumpdp", help="Datapath Name")
2669
2670    addflcmd = subparsers.add_parser("add-flow")
2671    addflcmd.add_argument("flbr", help="Datapath name")
2672    addflcmd.add_argument("flow", help="Flow specification")
2673    addflcmd.add_argument("acts", help="Flow actions")
2674
2675    delfscmd = subparsers.add_parser("del-flows")
2676    delfscmd.add_argument("flsbr", help="Datapath name")
2677
2678    subparsers.add_parser("psample-events")
2679
2680    args = parser.parse_args()
2681
2682    if args.verbose > 0:
2683        if args.verbose > 1:
2684            logging.basicConfig(level=logging.DEBUG)
2685
2686    ovspk = OvsPacket()
2687    ovsdp = OvsDatapath()
2688    ovsvp = OvsVport(ovspk)
2689    ovsflow = OvsFlow()
2690    ndb = NDB()
2691
2692    sys.setrecursionlimit(100000)
2693
2694    if args.subcommand == "psample-events":
2695        PsampleEvent().read_samples()
2696
2697    if hasattr(args, "showdp"):
2698        found = False
2699        for iface in ndb.interfaces:
2700            rep = None
2701            if args.showdp is None:
2702                rep = ovsdp.info(iface.ifname, 0)
2703            elif args.showdp == iface.ifname:
2704                rep = ovsdp.info(iface.ifname, 0)
2705
2706            if rep is not None:
2707                found = True
2708                print_ovsdp_full(rep, iface.index, ndb, ovsvp)
2709
2710        if not found:
2711            msg = "No DP found"
2712            if args.showdp is not None:
2713                msg += ":'%s'" % args.showdp
2714            print(msg)
2715    elif hasattr(args, "adddp"):
2716        rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk)
2717        if rep is None:
2718            print("DP '%s' already exists" % args.adddp)
2719        else:
2720            print("DP '%s' added" % args.adddp)
2721        if args.upcall:
2722            ovspk.upcall_handler(ovsflow)
2723    elif hasattr(args, "deldp"):
2724        ovsdp.destroy(args.deldp)
2725    elif hasattr(args, "addif"):
2726        rep = ovsdp.info(args.dpname, 0)
2727        if rep is None:
2728            print("DP '%s' not found." % args.dpname)
2729            return 1
2730        dpindex = rep["dpifindex"]
2731        rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype,
2732                           args.dport, args.lwt)
2733        msg = "vport '%s'" % args.addif
2734        if rep and rep["header"]["error"] is None:
2735            msg += " added."
2736        else:
2737            msg += " failed to add."
2738        if args.upcall:
2739            if rep is None:
2740                rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk)
2741            ovsvp.upcall_handler(ovsflow)
2742    elif hasattr(args, "delif"):
2743        rep = ovsdp.info(args.dpname, 0)
2744        if rep is None:
2745            print("DP '%s' not found." % args.dpname)
2746            return 1
2747        rep = ovsvp.detach(rep["dpifindex"], args.delif)
2748        msg = "vport '%s'" % args.delif
2749        if rep and rep["header"]["error"] is None:
2750            msg += " removed."
2751        else:
2752            msg += " failed to remove."
2753        if args.dellink:
2754            ipr = pyroute2.iproute.IPRoute()
2755            ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0])
2756    elif hasattr(args, "dumpdp"):
2757        rep = ovsdp.info(args.dumpdp, 0)
2758        if rep is None:
2759            print("DP '%s' not found." % args.dumpdp)
2760            return 1
2761        rep = ovsflow.dump(rep["dpifindex"])
2762        for flow in rep:
2763            print(flow.dpstr(True if args.verbose > 0 else False))
2764    elif hasattr(args, "flbr"):
2765        rep = ovsdp.info(args.flbr, 0)
2766        if rep is None:
2767            print("DP '%s' not found." % args.flbr)
2768            return 1
2769        flow = OvsFlow.ovs_flow_msg()
2770        flow.parse(args.flow, args.acts, rep["dpifindex"])
2771        ovsflow.add_flow(rep["dpifindex"], flow)
2772    elif hasattr(args, "flsbr"):
2773        rep = ovsdp.info(args.flsbr, 0)
2774        if rep is None:
2775            print("DP '%s' not found." % args.flsbr)
2776        ovsflow.del_flows(rep["dpifindex"])
2777
2778    return 0
2779
2780
2781if __name__ == "__main__":
2782    sys.exit(main(sys.argv))
2783