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