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