xref: /linux/tools/testing/selftests/net/openvswitch/ovs-dpctl.py (revision c7815abbea457afa2a1ab3ded46d29c19da377d2)
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_EGRESS_TUN_PORT") is not None:
593                print_str += "egress_tun_port=%d" % self.get_attr(
594                    "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT"
595                )
596            print_str += ")"
597            return print_str
598
599        def parse(self, actstr):
600            attrs_desc = (
601                ("pid", "OVS_USERSPACE_ATTR_PID", int),
602                ("userdata", "OVS_USERSPACE_ATTR_USERDATA",
603                    lambda x: list(bytearray.fromhex(x))),
604                ("egress_tun_port", "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", int)
605            )
606
607            attrs, actstr = parse_attrs(actstr, attrs_desc)
608            for attr in attrs:
609                self["attrs"].append(attr)
610
611            return actstr
612
613    def dpstr(self, more=False):
614        print_str = ""
615
616        for field in self["attrs"]:
617            if field[1] == "none" or self.get_attr(field[0]) is None:
618                continue
619            if print_str != "":
620                print_str += ","
621
622            if field[0] == "OVS_ACTION_ATTR_OUTPUT":
623                print_str += "%d" % int(self.get_attr(field[0]))
624            elif field[0] == "OVS_ACTION_ATTR_RECIRC":
625                print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))
626            elif field[0] == "OVS_ACTION_ATTR_TRUNC":
627                print_str += "trunc(%d)" % int(self.get_attr(field[0]))
628            elif field[0] == "OVS_ACTION_ATTR_DROP":
629                print_str += "drop(%d)" % int(self.get_attr(field[0]))
630            elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR":
631                print_str += "ct_clear"
632            elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":
633                print_str += "pop_vlan"
634            elif field[0] == "OVS_ACTION_ATTR_POP_ETH":
635                print_str += "pop_eth"
636            elif field[0] == "OVS_ACTION_ATTR_POP_NSH":
637                print_str += "pop_nsh"
638            elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":
639                print_str += "pop_mpls"
640            else:
641                datum = self.get_attr(field[0])
642                if field[0] == "OVS_ACTION_ATTR_CLONE":
643                    print_str += "clone("
644                    print_str += datum.dpstr(more)
645                    print_str += ")"
646                elif field[0] == "OVS_ACTION_ATTR_SET" or \
647                     field[0] == "OVS_ACTION_ATTR_SET_MASKED":
648                    print_str += "set"
649                    field = datum
650                    mask = None
651                    if field[0] == "OVS_ACTION_ATTR_SET_MASKED":
652                        print_str += "_masked"
653                        field = datum[0]
654                        mask = datum[1]
655                    print_str += "("
656                    print_str += field.dpstr(mask, more)
657                    print_str += ")"
658                else:
659                    try:
660                        print_str += datum.dpstr(more)
661                    except:
662                        print_str += "{ATTR: %s not decoded}" % field[0]
663
664        return print_str
665
666    def parse(self, actstr):
667        totallen = len(actstr)
668        while len(actstr) != 0:
669            parsed = False
670            parencount = 0
671            if actstr.startswith("drop"):
672                # If no reason is provided, the implicit drop is used (i.e no
673                # action). If some reason is given, an explicit action is used.
674                reason = None
675                if actstr.startswith("drop("):
676                    parencount += 1
677
678                    actstr, reason = parse_extract_field(
679                        actstr,
680                        "drop(",
681                        r"([0-9]+)",
682                        lambda x: int(x, 0),
683                        False,
684                        None,
685                    )
686
687                if reason is not None:
688                    self["attrs"].append(["OVS_ACTION_ATTR_DROP", reason])
689                    parsed = True
690                else:
691                    actstr = actstr[len("drop"): ]
692                    return (totallen - len(actstr))
693
694            elif parse_starts_block(actstr, r"^(\d+)", False, True):
695                actstr, output = parse_extract_field(
696                    actstr, None, r"(\d+)", lambda x: int(x), False, "0"
697                )
698                self["attrs"].append(["OVS_ACTION_ATTR_OUTPUT", output])
699                parsed = True
700            elif parse_starts_block(actstr, "recirc(", False):
701                actstr, recircid = parse_extract_field(
702                    actstr,
703                    "recirc(",
704                    r"([0-9a-fA-Fx]+)",
705                    lambda x: int(x, 0),
706                    False,
707                    0,
708                )
709                parencount += 1
710                self["attrs"].append(["OVS_ACTION_ATTR_RECIRC", recircid])
711                parsed = True
712
713            parse_flat_map = (
714                ("ct_clear", "OVS_ACTION_ATTR_CT_CLEAR"),
715                ("pop_vlan", "OVS_ACTION_ATTR_POP_VLAN"),
716                ("pop_eth", "OVS_ACTION_ATTR_POP_ETH"),
717                ("pop_nsh", "OVS_ACTION_ATTR_POP_NSH"),
718            )
719
720            for flat_act in parse_flat_map:
721                if parse_starts_block(actstr, flat_act[0], False):
722                    actstr = actstr[len(flat_act[0]):]
723                    self["attrs"].append([flat_act[1], True])
724                    actstr = actstr[strspn(actstr, ", ") :]
725                    parsed = True
726
727            if parse_starts_block(actstr, "clone(", False):
728                parencount += 1
729                subacts = ovsactions()
730                actstr = actstr[len("clone("):]
731                parsedLen = subacts.parse(actstr)
732                lst = []
733                self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts))
734                actstr = actstr[parsedLen:]
735                parsed = True
736            elif parse_starts_block(actstr, "set(", False):
737                parencount += 1
738                k = ovskey()
739                actstr = actstr[len("set("):]
740                actstr = k.parse(actstr, None)
741                self["attrs"].append(("OVS_ACTION_ATTR_SET", k))
742                if not actstr.startswith(")"):
743                    actstr = ")" + actstr
744                parsed = True
745            elif parse_starts_block(actstr, "set_masked(", False):
746                parencount += 1
747                k = ovskey()
748                m = ovskey()
749                actstr = actstr[len("set_masked("):]
750                actstr = k.parse(actstr, m)
751                self["attrs"].append(("OVS_ACTION_ATTR_SET_MASKED", [k, m]))
752                if not actstr.startswith(")"):
753                    actstr = ")" + actstr
754                parsed = True
755            elif parse_starts_block(actstr, "ct(", False):
756                parencount += 1
757                actstr = actstr[len("ct(") :]
758                ctact = ovsactions.ctact()
759
760                for scan in (
761                    ("commit", "OVS_CT_ATTR_COMMIT", None),
762                    ("force_commit", "OVS_CT_ATTR_FORCE_COMMIT", None),
763                    ("zone", "OVS_CT_ATTR_ZONE", int),
764                    ("mark", "OVS_CT_ATTR_MARK", int),
765                    ("helper", "OVS_CT_ATTR_HELPER", lambda x, y: str(x)),
766                    ("timeout", "OVS_CT_ATTR_TIMEOUT", lambda x, y: str(x)),
767                ):
768                    if actstr.startswith(scan[0]):
769                        actstr = actstr[len(scan[0]) :]
770                        if scan[2] is not None:
771                            if actstr[0] != "=":
772                                raise ValueError("Invalid ct attr")
773                            actstr = actstr[1:]
774                            pos = strcspn(actstr, ",)")
775                            datum = scan[2](actstr[:pos], 0)
776                            ctact["attrs"].append([scan[1], datum])
777                            actstr = actstr[pos:]
778                        else:
779                            ctact["attrs"].append([scan[1], None])
780                        actstr = actstr[strspn(actstr, ", ") :]
781                    # it seems strange to put this here, but nat() is a complex
782                    # sub-action and this lets it sit anywhere in the ct() action
783                    if actstr.startswith("nat"):
784                        actstr = actstr[3:]
785                        natact = ovsactions.ctact.natattr()
786
787                        if actstr.startswith("("):
788                            parencount += 1
789                            t = None
790                            actstr = actstr[1:]
791                            if actstr.startswith("src"):
792                                t = "OVS_NAT_ATTR_SRC"
793                                actstr = actstr[3:]
794                            elif actstr.startswith("dst"):
795                                t = "OVS_NAT_ATTR_DST"
796                                actstr = actstr[3:]
797
798                            actstr, ip_block_min = parse_extract_field(
799                                actstr, "=", r"([0-9a-fA-F\.]+)", str, False
800                            )
801                            actstr, ip_block_max = parse_extract_field(
802                                actstr, "-", r"([0-9a-fA-F\.]+)", str, False
803                            )
804
805                            actstr, proto_min = parse_extract_field(
806                                actstr, ":", r"(\d+)", int, False
807                            )
808                            actstr, proto_max = parse_extract_field(
809                                actstr, "-", r"(\d+)", int, False
810                            )
811
812                            if t is not None:
813                                natact["attrs"].append([t, None])
814
815                                if ip_block_min is not None:
816                                    natact["attrs"].append(
817                                        ["OVS_NAT_ATTR_IP_MIN", ip_block_min]
818                                    )
819                                if ip_block_max is not None:
820                                    natact["attrs"].append(
821                                        ["OVS_NAT_ATTR_IP_MAX", ip_block_max]
822                                    )
823                                if proto_min is not None:
824                                    natact["attrs"].append(
825                                        ["OVS_NAT_ATTR_PROTO_MIN", proto_min]
826                                    )
827                                if proto_max is not None:
828                                    natact["attrs"].append(
829                                        ["OVS_NAT_ATTR_PROTO_MAX", proto_max]
830                                    )
831
832                            for natscan in (
833                                ("persistent", "OVS_NAT_ATTR_PERSISTENT"),
834                                ("hash", "OVS_NAT_ATTR_PROTO_HASH"),
835                                ("random", "OVS_NAT_ATTR_PROTO_RANDOM"),
836                            ):
837                                if actstr.startswith(natscan[0]):
838                                    actstr = actstr[len(natscan[0]) :]
839                                    natact["attrs"].append([natscan[1], None])
840                                    actstr = actstr[strspn(actstr, ", ") :]
841
842                        ctact["attrs"].append(["OVS_CT_ATTR_NAT", natact])
843                        actstr = actstr[strspn(actstr, ", ") :]
844
845                self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact])
846                parsed = True
847
848            elif parse_starts_block(actstr, "sample(", False):
849                sampleact = self.sample()
850                actstr = sampleact.parse(actstr[len("sample(") : ])
851                self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact])
852                parsed = True
853
854            elif parse_starts_block(actstr, "psample(", False):
855                psampleact = self.psample()
856                actstr = psampleact.parse(actstr[len("psample(") : ])
857                self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact])
858                parsed = True
859
860            elif parse_starts_block(actstr, "userspace(", False):
861                uact = self.userspace()
862                actstr = uact.parse(actstr[len("userspace(") : ])
863                self["attrs"].append(["OVS_ACTION_ATTR_USERSPACE", uact])
864                parsed = True
865
866            actstr = actstr[strspn(actstr, ", ") :]
867            while parencount > 0:
868                parencount -= 1
869                actstr = actstr[strspn(actstr, " "):]
870                if len(actstr) and actstr[0] != ")":
871                    raise ValueError("Action str: '%s' unbalanced" % actstr)
872                actstr = actstr[1:]
873
874            if len(actstr) and actstr[0] == ")":
875                return (totallen - len(actstr))
876
877            actstr = actstr[strspn(actstr, ", ") :]
878
879            if not parsed:
880                raise ValueError("Action str: '%s' not supported" % actstr)
881
882        return (totallen - len(actstr))
883
884
885class ovskey(nla):
886    nla_flags = NLA_F_NESTED
887    nla_map = (
888        ("OVS_KEY_ATTR_UNSPEC", "none"),
889        ("OVS_KEY_ATTR_ENCAP", "none"),
890        ("OVS_KEY_ATTR_PRIORITY", "uint32"),
891        ("OVS_KEY_ATTR_IN_PORT", "uint32"),
892        ("OVS_KEY_ATTR_ETHERNET", "ethaddr"),
893        ("OVS_KEY_ATTR_VLAN", "uint16"),
894        ("OVS_KEY_ATTR_ETHERTYPE", "be16"),
895        ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"),
896        ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"),
897        ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"),
898        ("OVS_KEY_ATTR_UDP", "ovs_key_udp"),
899        ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"),
900        ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"),
901        ("OVS_KEY_ATTR_ARP", "ovs_key_arp"),
902        ("OVS_KEY_ATTR_ND", "ovs_key_nd"),
903        ("OVS_KEY_ATTR_SKB_MARK", "uint32"),
904        ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"),
905        ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"),
906        ("OVS_KEY_ATTR_TCP_FLAGS", "be16"),
907        ("OVS_KEY_ATTR_DP_HASH", "uint32"),
908        ("OVS_KEY_ATTR_RECIRC_ID", "uint32"),
909        ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"),
910        ("OVS_KEY_ATTR_CT_STATE", "uint32"),
911        ("OVS_KEY_ATTR_CT_ZONE", "uint16"),
912        ("OVS_KEY_ATTR_CT_MARK", "uint32"),
913        ("OVS_KEY_ATTR_CT_LABELS", "none"),
914        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"),
915        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"),
916        ("OVS_KEY_ATTR_NSH", "none"),
917        ("OVS_KEY_ATTR_PACKET_TYPE", "none"),
918        ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"),
919        ("OVS_KEY_ATTR_TUNNEL_INFO", "none"),
920        ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"),
921    )
922
923    class ovs_key_proto(nla):
924        fields = (
925            ("src", "!H"),
926            ("dst", "!H"),
927        )
928
929        fields_map = (
930            ("src", "src", "%d", lambda x: int(x) if x else 0,
931                convert_int(16)),
932            ("dst", "dst", "%d", lambda x: int(x) if x else 0,
933                convert_int(16)),
934        )
935
936        def __init__(
937            self,
938            protostr,
939            data=None,
940            offset=None,
941            parent=None,
942            length=None,
943            init=None,
944        ):
945            self.proto_str = protostr
946            nla.__init__(
947                self,
948                data=data,
949                offset=offset,
950                parent=parent,
951                length=length,
952                init=init,
953            )
954
955        def parse(self, flowstr, typeInst):
956            if not flowstr.startswith(self.proto_str):
957                return None, None
958
959            k = typeInst()
960            m = typeInst()
961
962            flowstr = flowstr[len(self.proto_str) :]
963            if flowstr.startswith("("):
964                flowstr = flowstr[1:]
965
966            keybits = b""
967            maskbits = b""
968            for f in self.fields_map:
969                if flowstr.startswith(f[1]):
970                    # the following assumes that the field looks
971                    # something like 'field.' where '.' is a
972                    # character that we don't exactly care about.
973                    flowstr = flowstr[len(f[1]) + 1 :]
974                    splitchar = 0
975                    for c in flowstr:
976                        if c == "," or c == ")":
977                            break
978                        splitchar += 1
979                    data = flowstr[:splitchar]
980                    flowstr = flowstr[splitchar:]
981                else:
982                    data = ""
983
984                if len(f) > 4:
985                    k[f[0]], m[f[0]] = f[4](data)
986                else:
987                    k[f[0]] = f[3](data)
988                    m[f[0]] = f[3](data)
989
990                flowstr = flowstr[strspn(flowstr, ", ") :]
991                if len(flowstr) == 0:
992                    return flowstr, k, m
993
994            flowstr = flowstr[strspn(flowstr, "), ") :]
995
996            return flowstr, k, m
997
998        def dpstr(self, masked=None, more=False):
999            outstr = self.proto_str + "("
1000            first = False
1001            for f in self.fields_map:
1002                if first:
1003                    outstr += ","
1004                if masked is None:
1005                    outstr += "%s=" % f[0]
1006                    if isinstance(f[2], str):
1007                        outstr += f[2] % self[f[1]]
1008                    else:
1009                        outstr += f[2](self[f[1]])
1010                    first = True
1011                elif more or f[3](masked[f[1]]) != 0:
1012                    outstr += "%s=" % f[0]
1013                    if isinstance(f[2], str):
1014                        outstr += f[2] % self[f[1]]
1015                    else:
1016                        outstr += f[2](self[f[1]])
1017                    outstr += "/"
1018                    if isinstance(f[2], str):
1019                        outstr += f[2] % masked[f[1]]
1020                    else:
1021                        outstr += f[2](masked[f[1]])
1022                    first = True
1023            outstr += ")"
1024            return outstr
1025
1026    class ethaddr(ovs_key_proto):
1027        fields = (
1028            ("src", "!6s"),
1029            ("dst", "!6s"),
1030        )
1031
1032        fields_map = (
1033            (
1034                "src",
1035                "src",
1036                macstr,
1037                lambda x: int.from_bytes(x, "big"),
1038                convert_mac,
1039            ),
1040            (
1041                "dst",
1042                "dst",
1043                macstr,
1044                lambda x: int.from_bytes(x, "big"),
1045                convert_mac,
1046            ),
1047        )
1048
1049        def __init__(
1050            self,
1051            data=None,
1052            offset=None,
1053            parent=None,
1054            length=None,
1055            init=None,
1056        ):
1057            ovskey.ovs_key_proto.__init__(
1058                self,
1059                "eth",
1060                data=data,
1061                offset=offset,
1062                parent=parent,
1063                length=length,
1064                init=init,
1065            )
1066
1067    class ovs_key_ipv4(ovs_key_proto):
1068        fields = (
1069            ("src", "!I"),
1070            ("dst", "!I"),
1071            ("proto", "B"),
1072            ("tos", "B"),
1073            ("ttl", "B"),
1074            ("frag", "B"),
1075        )
1076
1077        fields_map = (
1078            (
1079                "src",
1080                "src",
1081                lambda x: str(ipaddress.IPv4Address(x)),
1082                int,
1083                convert_ipv4,
1084            ),
1085            (
1086                "dst",
1087                "dst",
1088                lambda x: str(ipaddress.IPv4Address(x)),
1089                int,
1090                convert_ipv4,
1091            ),
1092            ("proto", "proto", "%d", lambda x: int(x) if x else 0,
1093                convert_int(8)),
1094            ("tos", "tos", "%d", lambda x: int(x) if x else 0,
1095                convert_int(8)),
1096            ("ttl", "ttl", "%d", lambda x: int(x) if x else 0,
1097                convert_int(8)),
1098            ("frag", "frag", "%d", lambda x: int(x) if x else 0,
1099                convert_int(8)),
1100        )
1101
1102        def __init__(
1103            self,
1104            data=None,
1105            offset=None,
1106            parent=None,
1107            length=None,
1108            init=None,
1109        ):
1110            ovskey.ovs_key_proto.__init__(
1111                self,
1112                "ipv4",
1113                data=data,
1114                offset=offset,
1115                parent=parent,
1116                length=length,
1117                init=init,
1118            )
1119
1120    class ovs_key_ipv6(ovs_key_proto):
1121        fields = (
1122            ("src", "!16s"),
1123            ("dst", "!16s"),
1124            ("label", "!I"),
1125            ("proto", "B"),
1126            ("tclass", "B"),
1127            ("hlimit", "B"),
1128            ("frag", "B"),
1129        )
1130
1131        fields_map = (
1132            (
1133                "src",
1134                "src",
1135                lambda x: str(ipaddress.IPv6Address(x)),
1136                lambda x: ipaddress.IPv6Address(x).packed if x else 0,
1137                convert_ipv6,
1138            ),
1139            (
1140                "dst",
1141                "dst",
1142                lambda x: str(ipaddress.IPv6Address(x)),
1143                lambda x: ipaddress.IPv6Address(x).packed if x else 0,
1144                convert_ipv6,
1145            ),
1146            ("label", "label", "%d", lambda x: int(x) if x else 0),
1147            ("proto", "proto", "%d", lambda x: int(x) if x else 0),
1148            ("tclass", "tclass", "%d", lambda x: int(x) if x else 0),
1149            ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0),
1150            ("frag", "frag", "%d", lambda x: int(x) if x else 0),
1151        )
1152
1153        def __init__(
1154            self,
1155            data=None,
1156            offset=None,
1157            parent=None,
1158            length=None,
1159            init=None,
1160        ):
1161            ovskey.ovs_key_proto.__init__(
1162                self,
1163                "ipv6",
1164                data=data,
1165                offset=offset,
1166                parent=parent,
1167                length=length,
1168                init=init,
1169            )
1170
1171    class ovs_key_tcp(ovs_key_proto):
1172        def __init__(
1173            self,
1174            data=None,
1175            offset=None,
1176            parent=None,
1177            length=None,
1178            init=None,
1179        ):
1180            ovskey.ovs_key_proto.__init__(
1181                self,
1182                "tcp",
1183                data=data,
1184                offset=offset,
1185                parent=parent,
1186                length=length,
1187                init=init,
1188            )
1189
1190    class ovs_key_udp(ovs_key_proto):
1191        def __init__(
1192            self,
1193            data=None,
1194            offset=None,
1195            parent=None,
1196            length=None,
1197            init=None,
1198        ):
1199            ovskey.ovs_key_proto.__init__(
1200                self,
1201                "udp",
1202                data=data,
1203                offset=offset,
1204                parent=parent,
1205                length=length,
1206                init=init,
1207            )
1208
1209    class ovs_key_sctp(ovs_key_proto):
1210        def __init__(
1211            self,
1212            data=None,
1213            offset=None,
1214            parent=None,
1215            length=None,
1216            init=None,
1217        ):
1218            ovskey.ovs_key_proto.__init__(
1219                self,
1220                "sctp",
1221                data=data,
1222                offset=offset,
1223                parent=parent,
1224                length=length,
1225                init=init,
1226            )
1227
1228    class ovs_key_icmp(ovs_key_proto):
1229        fields = (
1230            ("type", "B"),
1231            ("code", "B"),
1232        )
1233
1234        fields_map = (
1235            ("type", "type", "%d", lambda x: int(x) if x else 0),
1236            ("code", "code", "%d", lambda x: int(x) if x else 0),
1237        )
1238
1239        def __init__(
1240            self,
1241            data=None,
1242            offset=None,
1243            parent=None,
1244            length=None,
1245            init=None,
1246        ):
1247            ovskey.ovs_key_proto.__init__(
1248                self,
1249                "icmp",
1250                data=data,
1251                offset=offset,
1252                parent=parent,
1253                length=length,
1254                init=init,
1255            )
1256
1257    class ovs_key_icmpv6(ovs_key_icmp):
1258        def __init__(
1259            self,
1260            data=None,
1261            offset=None,
1262            parent=None,
1263            length=None,
1264            init=None,
1265        ):
1266            ovskey.ovs_key_proto.__init__(
1267                self,
1268                "icmpv6",
1269                data=data,
1270                offset=offset,
1271                parent=parent,
1272                length=length,
1273                init=init,
1274            )
1275
1276    class ovs_key_arp(ovs_key_proto):
1277        fields = (
1278            ("sip", "!I"),
1279            ("tip", "!I"),
1280            ("op", "!H"),
1281            ("sha", "!6s"),
1282            ("tha", "!6s"),
1283            ("pad", "xx"),
1284        )
1285
1286        fields_map = (
1287            (
1288                "sip",
1289                "sip",
1290                lambda x: str(ipaddress.IPv4Address(x)),
1291                int,
1292                convert_ipv4,
1293            ),
1294            (
1295                "tip",
1296                "tip",
1297                lambda x: str(ipaddress.IPv4Address(x)),
1298                int,
1299                convert_ipv4,
1300            ),
1301            ("op", "op", "%d", lambda x: int(x) if x else 0),
1302            (
1303                "sha",
1304                "sha",
1305                macstr,
1306                lambda x: int.from_bytes(x, "big"),
1307                convert_mac,
1308            ),
1309            (
1310                "tha",
1311                "tha",
1312                macstr,
1313                lambda x: int.from_bytes(x, "big"),
1314                convert_mac,
1315            ),
1316        )
1317
1318        def __init__(
1319            self,
1320            data=None,
1321            offset=None,
1322            parent=None,
1323            length=None,
1324            init=None,
1325        ):
1326            ovskey.ovs_key_proto.__init__(
1327                self,
1328                "arp",
1329                data=data,
1330                offset=offset,
1331                parent=parent,
1332                length=length,
1333                init=init,
1334            )
1335
1336    class ovs_key_nd(ovs_key_proto):
1337        fields = (
1338            ("target", "!16s"),
1339            ("sll", "!6s"),
1340            ("tll", "!6s"),
1341        )
1342
1343        fields_map = (
1344            (
1345                "target",
1346                "target",
1347                lambda x: str(ipaddress.IPv6Address(x)),
1348                convert_ipv6,
1349            ),
1350            ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")),
1351            ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")),
1352        )
1353
1354        def __init__(
1355            self,
1356            data=None,
1357            offset=None,
1358            parent=None,
1359            length=None,
1360            init=None,
1361        ):
1362            ovskey.ovs_key_proto.__init__(
1363                self,
1364                "nd",
1365                data=data,
1366                offset=offset,
1367                parent=parent,
1368                length=length,
1369                init=init,
1370            )
1371
1372    class ovs_key_ct_tuple_ipv4(ovs_key_proto):
1373        fields = (
1374            ("src", "!I"),
1375            ("dst", "!I"),
1376            ("tp_src", "!H"),
1377            ("tp_dst", "!H"),
1378            ("proto", "B"),
1379        )
1380
1381        fields_map = (
1382            (
1383                "src",
1384                "src",
1385                lambda x: str(ipaddress.IPv4Address(x)),
1386                int,
1387                convert_ipv4,
1388            ),
1389            (
1390                "dst",
1391                "dst",
1392                lambda x: str(ipaddress.IPv4Address(x)),
1393                int,
1394                convert_ipv4,
1395            ),
1396            ("tp_src", "tp_src", "%d", int),
1397            ("tp_dst", "tp_dst", "%d", int),
1398            ("proto", "proto", "%d", int),
1399        )
1400
1401        def __init__(
1402            self,
1403            data=None,
1404            offset=None,
1405            parent=None,
1406            length=None,
1407            init=None,
1408        ):
1409            ovskey.ovs_key_proto.__init__(
1410                self,
1411                "ct_tuple4",
1412                data=data,
1413                offset=offset,
1414                parent=parent,
1415                length=length,
1416                init=init,
1417            )
1418
1419    class ovs_key_ct_tuple_ipv6(nla):
1420        fields = (
1421            ("src", "!16s"),
1422            ("dst", "!16s"),
1423            ("tp_src", "!H"),
1424            ("tp_dst", "!H"),
1425            ("proto", "B"),
1426        )
1427
1428        fields_map = (
1429            (
1430                "src",
1431                "src",
1432                lambda x: str(ipaddress.IPv6Address(x)),
1433                convert_ipv6,
1434            ),
1435            (
1436                "dst",
1437                "dst",
1438                lambda x: str(ipaddress.IPv6Address(x)),
1439                convert_ipv6,
1440            ),
1441            ("tp_src", "tp_src", "%d", int),
1442            ("tp_dst", "tp_dst", "%d", int),
1443            ("proto", "proto", "%d", int),
1444        )
1445
1446        def __init__(
1447            self,
1448            data=None,
1449            offset=None,
1450            parent=None,
1451            length=None,
1452            init=None,
1453        ):
1454            ovskey.ovs_key_proto.__init__(
1455                self,
1456                "ct_tuple6",
1457                data=data,
1458                offset=offset,
1459                parent=parent,
1460                length=length,
1461                init=init,
1462            )
1463
1464    class ovs_key_tunnel(nla):
1465        nla_flags = NLA_F_NESTED
1466
1467        nla_map = (
1468            ("OVS_TUNNEL_KEY_ATTR_ID", "be64"),
1469            ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"),
1470            ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"),
1471            ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"),
1472            ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"),
1473            ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"),
1474            ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"),
1475            ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"),
1476            ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"),
1477            ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"),
1478            ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"),
1479            ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"),
1480            ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"),
1481            ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"),
1482            ("OVS_TUNNEL_KEY_ATTR_PAD", "none"),
1483            ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"),
1484            ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"),
1485        )
1486
1487        def parse(self, flowstr, mask=None):
1488            if not flowstr.startswith("tunnel("):
1489                return None, None
1490
1491            k = ovskey.ovs_key_tunnel()
1492            if mask is not None:
1493                mask = ovskey.ovs_key_tunnel()
1494
1495            flowstr = flowstr[len("tunnel("):]
1496
1497            v6_address = None
1498
1499            fields = [
1500                ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID",
1501                 0xffffffffffffffff, None, None),
1502
1503                ("src=", r"([0-9a-fA-F\.]+)", str,
1504                 "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0",
1505                 False),
1506                ("dst=", r"([0-9a-fA-F\.]+)", str,
1507                 "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0",
1508                 False),
1509
1510                ("ipv6_src=", r"([0-9a-fA-F:]+)", str,
1511                 "OVS_TUNNEL_KEY_ATTR_IPV6_SRC",
1512                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),
1513                ("ipv6_dst=", r"([0-9a-fA-F:]+)", str,
1514                 "OVS_TUNNEL_KEY_ATTR_IPV6_DST",
1515                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),
1516
1517                ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0,
1518                 None),
1519                ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0,
1520                 None),
1521
1522                ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC",
1523                 65535, 0, None),
1524                ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST",
1525                 65535, 0, None),
1526            ]
1527
1528            forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"]
1529
1530            for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields:
1531                flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False)
1532                if not attr_name:
1533                    raise Exception("Bad list value in tunnel fields")
1534
1535                if value is None and attr_name in forced_include:
1536                    value = default_val
1537                    mask_val = default_val
1538
1539                if value is not None:
1540                    if v46_flag is not None:
1541                        if v6_address is None:
1542                            v6_address = v46_flag
1543                        if v46_flag != v6_address:
1544                            raise ValueError("Cannot mix v6 and v4 addresses")
1545                    k["attrs"].append([attr_name, value])
1546                    if mask is not None:
1547                        mask["attrs"].append([attr_name, mask_val])
1548                else:
1549                    if v46_flag is not None:
1550                        if v6_address is None or v46_flag != v6_address:
1551                            continue
1552                    if mask is not None:
1553                        mask["attrs"].append([attr_name, default_val])
1554
1555            if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID":
1556                raise ValueError("Needs a tunid set")
1557
1558            if flowstr.startswith("flags("):
1559                flowstr = flowstr[len("flags("):]
1560                flagspos = flowstr.find(")")
1561                flags = flowstr[:flagspos]
1562                flowstr = flowstr[flagspos + 1:]
1563
1564                flag_attrs = {
1565                    "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT",
1566                    "csum": "OVS_TUNNEL_KEY_ATTR_CSUM",
1567                    "oam": "OVS_TUNNEL_KEY_ATTR_OAM"
1568                }
1569
1570                for flag in flags.split("|"):
1571                    if flag in flag_attrs:
1572                        k["attrs"].append([flag_attrs[flag], True])
1573                        if mask is not None:
1574                            mask["attrs"].append([flag_attrs[flag], True])
1575
1576            flowstr = flowstr[strspn(flowstr, ", ") :]
1577            return flowstr, k, mask
1578
1579        def dpstr(self, mask=None, more=False):
1580            print_str = "tunnel("
1581
1582            flagsattrs = []
1583            for k in self["attrs"]:
1584                noprint = False
1585                if k[0] == "OVS_TUNNEL_KEY_ATTR_ID":
1586                    print_str += "tun_id=%d" % k[1]
1587                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC":
1588                    print_str += "src=%s" % k[1]
1589                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST":
1590                    print_str += "dst=%s" % k[1]
1591                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC":
1592                    print_str += "ipv6_src=%s" % k[1]
1593                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST":
1594                    print_str += "ipv6_dst=%s" % k[1]
1595                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS":
1596                    print_str += "tos=%d" % k[1]
1597                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL":
1598                    print_str += "ttl=%d" % k[1]
1599                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC":
1600                    print_str += "tp_src=%d" % k[1]
1601                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST":
1602                    print_str += "tp_dst=%d" % k[1]
1603                elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT":
1604                    noprint = True
1605                    flagsattrs.append("df")
1606                elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM":
1607                    noprint = True
1608                    flagsattrs.append("csum")
1609                elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM":
1610                    noprint = True
1611                    flagsattrs.append("oam")
1612
1613                if not noprint:
1614                    print_str += ","
1615
1616            if len(flagsattrs):
1617                print_str += "flags(" + "|".join(flagsattrs) + ")"
1618            print_str += ")"
1619            return print_str
1620
1621    class ovs_key_mpls(nla):
1622        fields = (("lse", ">I"),)
1623
1624    def parse(self, flowstr, mask=None):
1625        for field in (
1626            ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse),
1627            ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse),
1628            ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse),
1629            ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel),
1630            ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse),
1631            ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state),
1632            ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse),
1633            ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse),
1634            ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse),
1635            (
1636                "OVS_KEY_ATTR_ETHERNET",
1637                "eth",
1638                ovskey.ethaddr,
1639            ),
1640            (
1641                "OVS_KEY_ATTR_ETHERTYPE",
1642                "eth_type",
1643                lambda x: intparse(x, "0xffff"),
1644            ),
1645            (
1646                "OVS_KEY_ATTR_IPV4",
1647                "ipv4",
1648                ovskey.ovs_key_ipv4,
1649            ),
1650            (
1651                "OVS_KEY_ATTR_IPV6",
1652                "ipv6",
1653                ovskey.ovs_key_ipv6,
1654            ),
1655            (
1656                "OVS_KEY_ATTR_ARP",
1657                "arp",
1658                ovskey.ovs_key_arp,
1659            ),
1660            (
1661                "OVS_KEY_ATTR_TCP",
1662                "tcp",
1663                ovskey.ovs_key_tcp,
1664            ),
1665            (
1666                "OVS_KEY_ATTR_UDP",
1667                "udp",
1668                ovskey.ovs_key_udp,
1669            ),
1670            (
1671                "OVS_KEY_ATTR_ICMP",
1672                "icmp",
1673                ovskey.ovs_key_icmp,
1674            ),
1675            (
1676                "OVS_KEY_ATTR_TCP_FLAGS",
1677                "tcp_flags",
1678                lambda x: parse_flags(x, None),
1679            ),
1680        ):
1681            fld = field[1] + "("
1682            if not flowstr.startswith(fld):
1683                continue
1684
1685            if not isinstance(field[2], types.FunctionType):
1686                nk = field[2]()
1687                flowstr, k, m = nk.parse(flowstr, field[2])
1688            else:
1689                flowstr = flowstr[len(fld) :]
1690                flowstr, k, m = field[2](flowstr)
1691
1692            if m and mask is not None:
1693                mask["attrs"].append([field[0], m])
1694            self["attrs"].append([field[0], k])
1695
1696            flowstr = flowstr[strspn(flowstr, "), ") :]
1697
1698        return flowstr
1699
1700    def dpstr(self, mask=None, more=False):
1701        print_str = ""
1702
1703        for field in (
1704            (
1705                "OVS_KEY_ATTR_PRIORITY",
1706                "skb_priority",
1707                "%d",
1708                lambda x: False,
1709                True,
1710            ),
1711            (
1712                "OVS_KEY_ATTR_SKB_MARK",
1713                "skb_mark",
1714                "%d",
1715                lambda x: False,
1716                True,
1717            ),
1718            (
1719                "OVS_KEY_ATTR_RECIRC_ID",
1720                "recirc_id",
1721                "0x%08X",
1722                lambda x: False,
1723                True,
1724            ),
1725            (
1726                "OVS_KEY_ATTR_DP_HASH",
1727                "dp_hash",
1728                "0x%08X",
1729                lambda x: False,
1730                True,
1731            ),
1732            (
1733                "OVS_KEY_ATTR_TUNNEL",
1734                "tunnel",
1735                None,
1736                False,
1737                False,
1738            ),
1739            (
1740                "OVS_KEY_ATTR_CT_STATE",
1741                "ct_state",
1742                "0x%04x",
1743                lambda x: False,
1744                True,
1745            ),
1746            (
1747                "OVS_KEY_ATTR_CT_ZONE",
1748                "ct_zone",
1749                "0x%04x",
1750                lambda x: False,
1751                True,
1752            ),
1753            (
1754                "OVS_KEY_ATTR_CT_MARK",
1755                "ct_mark",
1756                "0x%08x",
1757                lambda x: False,
1758                True,
1759            ),
1760            (
1761                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4",
1762                None,
1763                None,
1764                False,
1765                False,
1766            ),
1767            (
1768                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6",
1769                None,
1770                None,
1771                False,
1772                False,
1773            ),
1774            (
1775                "OVS_KEY_ATTR_IN_PORT",
1776                "in_port",
1777                "%d",
1778                lambda x: True,
1779                True,
1780            ),
1781            ("OVS_KEY_ATTR_ETHERNET", None, None, False, False),
1782            (
1783                "OVS_KEY_ATTR_ETHERTYPE",
1784                "eth_type",
1785                "0x%04x",
1786                lambda x: int(x) == 0xFFFF,
1787                True,
1788            ),
1789            ("OVS_KEY_ATTR_IPV4", None, None, False, False),
1790            ("OVS_KEY_ATTR_IPV6", None, None, False, False),
1791            ("OVS_KEY_ATTR_ARP", None, None, False, False),
1792            ("OVS_KEY_ATTR_TCP", None, None, False, False),
1793            (
1794                "OVS_KEY_ATTR_TCP_FLAGS",
1795                "tcp_flags",
1796                "0x%04x",
1797                lambda x: False,
1798                True,
1799            ),
1800            ("OVS_KEY_ATTR_UDP", None, None, False, False),
1801            ("OVS_KEY_ATTR_SCTP", None, None, False, False),
1802            ("OVS_KEY_ATTR_ICMP", None, None, False, False),
1803            ("OVS_KEY_ATTR_ICMPV6", None, None, False, False),
1804            ("OVS_KEY_ATTR_ND", None, None, False, False),
1805        ):
1806            v = self.get_attr(field[0])
1807            if v is not None:
1808                m = None if mask is None else mask.get_attr(field[0])
1809                if field[4] is False:
1810                    print_str += v.dpstr(m, more)
1811                    print_str += ","
1812                else:
1813                    if m is None or field[3](m):
1814                        print_str += field[1] + "("
1815                        print_str += field[2] % v
1816                        print_str += "),"
1817                    elif more or m != 0:
1818                        print_str += field[1] + "("
1819                        print_str += (field[2] % v) + "/" + (field[2] % m)
1820                        print_str += "),"
1821
1822        return print_str
1823
1824
1825class OvsPacket(GenericNetlinkSocket):
1826    OVS_PACKET_CMD_MISS = 1  # Flow table miss
1827    OVS_PACKET_CMD_ACTION = 2  # USERSPACE action
1828    OVS_PACKET_CMD_EXECUTE = 3  # Apply actions to packet
1829
1830    class ovs_packet_msg(ovs_dp_msg):
1831        nla_map = (
1832            ("OVS_PACKET_ATTR_UNSPEC", "none"),
1833            ("OVS_PACKET_ATTR_PACKET", "array(uint8)"),
1834            ("OVS_PACKET_ATTR_KEY", "ovskey"),
1835            ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"),
1836            ("OVS_PACKET_ATTR_USERDATA", "none"),
1837            ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"),
1838            ("OVS_PACKET_ATTR_UNUSED1", "none"),
1839            ("OVS_PACKET_ATTR_UNUSED2", "none"),
1840            ("OVS_PACKET_ATTR_PROBE", "none"),
1841            ("OVS_PACKET_ATTR_MRU", "uint16"),
1842            ("OVS_PACKET_ATTR_LEN", "uint32"),
1843            ("OVS_PACKET_ATTR_HASH", "uint64"),
1844        )
1845
1846    def __init__(self):
1847        GenericNetlinkSocket.__init__(self)
1848        self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg)
1849
1850    def upcall_handler(self, up=None):
1851        print("listening on upcall packet handler:", self.epid)
1852        while True:
1853            try:
1854                msgs = self.get()
1855                for msg in msgs:
1856                    if not up:
1857                        continue
1858                    if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS:
1859                        up.miss(msg)
1860                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION:
1861                        up.action(msg)
1862                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE:
1863                        up.execute(msg)
1864                    else:
1865                        print("Unkonwn cmd: %d" % msg["cmd"])
1866            except NetlinkError as ne:
1867                raise ne
1868
1869
1870class OvsDatapath(GenericNetlinkSocket):
1871    OVS_DP_F_VPORT_PIDS = 1 << 1
1872    OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
1873
1874    class dp_cmd_msg(ovs_dp_msg):
1875        """
1876        Message class that will be used to communicate with the kernel module.
1877        """
1878
1879        nla_map = (
1880            ("OVS_DP_ATTR_UNSPEC", "none"),
1881            ("OVS_DP_ATTR_NAME", "asciiz"),
1882            ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"),
1883            ("OVS_DP_ATTR_STATS", "dpstats"),
1884            ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"),
1885            ("OVS_DP_ATTR_USER_FEATURES", "uint32"),
1886            ("OVS_DP_ATTR_PAD", "none"),
1887            ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"),
1888            ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"),
1889        )
1890
1891        class dpstats(nla):
1892            fields = (
1893                ("hit", "=Q"),
1894                ("missed", "=Q"),
1895                ("lost", "=Q"),
1896                ("flows", "=Q"),
1897            )
1898
1899        class megaflowstats(nla):
1900            fields = (
1901                ("mask_hit", "=Q"),
1902                ("masks", "=I"),
1903                ("padding", "=I"),
1904                ("cache_hits", "=Q"),
1905                ("pad1", "=Q"),
1906            )
1907
1908    def __init__(self):
1909        GenericNetlinkSocket.__init__(self)
1910        self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg)
1911
1912    def info(self, dpname, ifindex=0):
1913        msg = OvsDatapath.dp_cmd_msg()
1914        msg["cmd"] = OVS_DP_CMD_GET
1915        msg["version"] = OVS_DATAPATH_VERSION
1916        msg["reserved"] = 0
1917        msg["dpifindex"] = ifindex
1918        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1919
1920        try:
1921            reply = self.nlm_request(
1922                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST
1923            )
1924            reply = reply[0]
1925        except NetlinkError as ne:
1926            if ne.code == errno.ENODEV:
1927                reply = None
1928            else:
1929                raise ne
1930
1931        return reply
1932
1933    def create(
1934        self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket()
1935    ):
1936        msg = OvsDatapath.dp_cmd_msg()
1937        msg["cmd"] = OVS_DP_CMD_NEW
1938        if versionStr is None:
1939            msg["version"] = OVS_DATAPATH_VERSION
1940        else:
1941            msg["version"] = int(versionStr.split(":")[0], 0)
1942        msg["reserved"] = 0
1943        msg["dpifindex"] = 0
1944        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1945
1946        dpfeatures = 0
1947        if versionStr is not None and versionStr.find(":") != -1:
1948            dpfeatures = int(versionStr.split(":")[1], 0)
1949        else:
1950            if versionStr is None or versionStr.find(":") == -1:
1951                dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU
1952                dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS
1953
1954            nproc = multiprocessing.cpu_count()
1955            procarray = []
1956            for i in range(1, nproc):
1957                procarray += [int(p.epid)]
1958            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray])
1959        msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures])
1960        if not shouldUpcall:
1961            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]])
1962
1963        try:
1964            reply = self.nlm_request(
1965                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1966            )
1967            reply = reply[0]
1968        except NetlinkError as ne:
1969            if ne.code == errno.EEXIST:
1970                reply = None
1971            else:
1972                raise ne
1973
1974        return reply
1975
1976    def destroy(self, dpname):
1977        msg = OvsDatapath.dp_cmd_msg()
1978        msg["cmd"] = OVS_DP_CMD_DEL
1979        msg["version"] = OVS_DATAPATH_VERSION
1980        msg["reserved"] = 0
1981        msg["dpifindex"] = 0
1982        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])
1983
1984        try:
1985            reply = self.nlm_request(
1986                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
1987            )
1988            reply = reply[0]
1989        except NetlinkError as ne:
1990            if ne.code == errno.ENODEV:
1991                reply = None
1992            else:
1993                raise ne
1994
1995        return reply
1996
1997
1998class OvsVport(GenericNetlinkSocket):
1999    OVS_VPORT_TYPE_NETDEV = 1
2000    OVS_VPORT_TYPE_INTERNAL = 2
2001    OVS_VPORT_TYPE_GRE = 3
2002    OVS_VPORT_TYPE_VXLAN = 4
2003    OVS_VPORT_TYPE_GENEVE = 5
2004
2005    class ovs_vport_msg(ovs_dp_msg):
2006        nla_map = (
2007            ("OVS_VPORT_ATTR_UNSPEC", "none"),
2008            ("OVS_VPORT_ATTR_PORT_NO", "uint32"),
2009            ("OVS_VPORT_ATTR_TYPE", "uint32"),
2010            ("OVS_VPORT_ATTR_NAME", "asciiz"),
2011            ("OVS_VPORT_ATTR_OPTIONS", "vportopts"),
2012            ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"),
2013            ("OVS_VPORT_ATTR_STATS", "vportstats"),
2014            ("OVS_VPORT_ATTR_PAD", "none"),
2015            ("OVS_VPORT_ATTR_IFINDEX", "uint32"),
2016            ("OVS_VPORT_ATTR_NETNSID", "uint32"),
2017        )
2018
2019        class vportopts(nla):
2020            nla_map = (
2021                ("OVS_TUNNEL_ATTR_UNSPEC", "none"),
2022                ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"),
2023                ("OVS_TUNNEL_ATTR_EXTENSION", "none"),
2024            )
2025
2026        class vportstats(nla):
2027            fields = (
2028                ("rx_packets", "=Q"),
2029                ("tx_packets", "=Q"),
2030                ("rx_bytes", "=Q"),
2031                ("tx_bytes", "=Q"),
2032                ("rx_errors", "=Q"),
2033                ("tx_errors", "=Q"),
2034                ("rx_dropped", "=Q"),
2035                ("tx_dropped", "=Q"),
2036            )
2037
2038    def type_to_str(vport_type):
2039        if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:
2040            return "netdev"
2041        elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:
2042            return "internal"
2043        elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE:
2044            return "gre"
2045        elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN:
2046            return "vxlan"
2047        elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE:
2048            return "geneve"
2049        raise ValueError("Unknown vport type:%d" % vport_type)
2050
2051    def str_to_type(vport_type):
2052        if vport_type == "netdev":
2053            return OvsVport.OVS_VPORT_TYPE_NETDEV
2054        elif vport_type == "internal":
2055            return OvsVport.OVS_VPORT_TYPE_INTERNAL
2056        elif vport_type == "gre":
2057            return OvsVport.OVS_VPORT_TYPE_INTERNAL
2058        elif vport_type == "vxlan":
2059            return OvsVport.OVS_VPORT_TYPE_VXLAN
2060        elif vport_type == "geneve":
2061            return OvsVport.OVS_VPORT_TYPE_GENEVE
2062        raise ValueError("Unknown vport type: '%s'" % vport_type)
2063
2064    def __init__(self, packet=OvsPacket()):
2065        GenericNetlinkSocket.__init__(self)
2066        self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg)
2067        self.upcall_packet = packet
2068
2069    def info(self, vport_name, dpifindex=0, portno=None):
2070        msg = OvsVport.ovs_vport_msg()
2071
2072        msg["cmd"] = OVS_VPORT_CMD_GET
2073        msg["version"] = OVS_DATAPATH_VERSION
2074        msg["reserved"] = 0
2075        msg["dpifindex"] = dpifindex
2076
2077        if portno is None:
2078            msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name])
2079        else:
2080            msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno])
2081
2082        try:
2083            reply = self.nlm_request(
2084                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST
2085            )
2086            reply = reply[0]
2087        except NetlinkError as ne:
2088            if ne.code == errno.ENODEV:
2089                reply = None
2090            else:
2091                raise ne
2092        return reply
2093
2094    def attach(self, dpindex, vport_ifname, ptype, dport, lwt):
2095        msg = OvsVport.ovs_vport_msg()
2096
2097        msg["cmd"] = OVS_VPORT_CMD_NEW
2098        msg["version"] = OVS_DATAPATH_VERSION
2099        msg["reserved"] = 0
2100        msg["dpifindex"] = dpindex
2101        port_type = OvsVport.str_to_type(ptype)
2102
2103        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
2104        msg["attrs"].append(
2105            ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]]
2106        )
2107
2108        TUNNEL_DEFAULTS = [("geneve", 6081),
2109                           ("vxlan", 4789)]
2110
2111        for tnl in TUNNEL_DEFAULTS:
2112            if ptype == tnl[0]:
2113                if not dport:
2114                    dport = tnl[1]
2115
2116                if not lwt:
2117                    vportopt = OvsVport.ovs_vport_msg.vportopts()
2118                    vportopt["attrs"].append(
2119                        ["OVS_TUNNEL_ATTR_DST_PORT", socket.htons(dport)]
2120                    )
2121                    msg["attrs"].append(
2122                        ["OVS_VPORT_ATTR_OPTIONS", vportopt]
2123                    )
2124                else:
2125                    port_type = OvsVport.OVS_VPORT_TYPE_NETDEV
2126                    ipr = pyroute2.iproute.IPRoute()
2127
2128                    if tnl[0] == "geneve":
2129                        ipr.link("add", ifname=vport_ifname, kind=tnl[0],
2130                                 geneve_port=dport,
2131                                 geneve_collect_metadata=True,
2132                                 geneve_udp_zero_csum6_rx=1)
2133                    elif tnl[0] == "vxlan":
2134                        ipr.link("add", ifname=vport_ifname, kind=tnl[0],
2135                                 vxlan_learning=0, vxlan_collect_metadata=1,
2136                                 vxlan_udp_zero_csum6_rx=1, vxlan_port=dport)
2137                break
2138        msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type])
2139
2140        try:
2141            reply = self.nlm_request(
2142                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
2143            )
2144            reply = reply[0]
2145        except NetlinkError as ne:
2146            if ne.code == errno.EEXIST:
2147                reply = None
2148            else:
2149                raise ne
2150        return reply
2151
2152    def reset_upcall(self, dpindex, vport_ifname, p=None):
2153        msg = OvsVport.ovs_vport_msg()
2154
2155        msg["cmd"] = OVS_VPORT_CMD_SET
2156        msg["version"] = OVS_DATAPATH_VERSION
2157        msg["reserved"] = 0
2158        msg["dpifindex"] = dpindex
2159        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
2160
2161        if p == None:
2162            p = self.upcall_packet
2163        else:
2164            self.upcall_packet = p
2165
2166        msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]])
2167
2168        try:
2169            reply = self.nlm_request(
2170                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
2171            )
2172            reply = reply[0]
2173        except NetlinkError as ne:
2174            raise ne
2175        return reply
2176
2177    def detach(self, dpindex, vport_ifname):
2178        msg = OvsVport.ovs_vport_msg()
2179
2180        msg["cmd"] = OVS_VPORT_CMD_DEL
2181        msg["version"] = OVS_DATAPATH_VERSION
2182        msg["reserved"] = 0
2183        msg["dpifindex"] = dpindex
2184        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
2185
2186        try:
2187            reply = self.nlm_request(
2188                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
2189            )
2190            reply = reply[0]
2191        except NetlinkError as ne:
2192            if ne.code == errno.ENODEV:
2193                reply = None
2194            else:
2195                raise ne
2196        return reply
2197
2198    def upcall_handler(self, handler=None):
2199        self.upcall_packet.upcall_handler(handler)
2200
2201
2202class OvsFlow(GenericNetlinkSocket):
2203    class ovs_flow_msg(ovs_dp_msg):
2204        nla_map = (
2205            ("OVS_FLOW_ATTR_UNSPEC", "none"),
2206            ("OVS_FLOW_ATTR_KEY", "ovskey"),
2207            ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"),
2208            ("OVS_FLOW_ATTR_STATS", "flowstats"),
2209            ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"),
2210            ("OVS_FLOW_ATTR_USED", "uint64"),
2211            ("OVS_FLOW_ATTR_CLEAR", "none"),
2212            ("OVS_FLOW_ATTR_MASK", "ovskey"),
2213            ("OVS_FLOW_ATTR_PROBE", "none"),
2214            ("OVS_FLOW_ATTR_UFID", "array(uint32)"),
2215            ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"),
2216        )
2217
2218        class flowstats(nla):
2219            fields = (
2220                ("packets", "=Q"),
2221                ("bytes", "=Q"),
2222            )
2223
2224        def dpstr(self, more=False):
2225            ufid = self.get_attr("OVS_FLOW_ATTR_UFID")
2226            ufid_str = ""
2227            if ufid is not None:
2228                ufid_str = (
2229                    "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(
2230                        ufid[0],
2231                        ufid[1] >> 16,
2232                        ufid[1] & 0xFFFF,
2233                        ufid[2] >> 16,
2234                        ufid[2] & 0,
2235                        ufid[3],
2236                    )
2237                )
2238
2239            key_field = self.get_attr("OVS_FLOW_ATTR_KEY")
2240            keymsg = None
2241            if key_field is not None:
2242                keymsg = key_field
2243
2244            mask_field = self.get_attr("OVS_FLOW_ATTR_MASK")
2245            maskmsg = None
2246            if mask_field is not None:
2247                maskmsg = mask_field
2248
2249            acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS")
2250            actsmsg = None
2251            if acts_field is not None:
2252                actsmsg = acts_field
2253
2254            print_str = ""
2255
2256            if more:
2257                print_str += ufid_str + ","
2258
2259            if keymsg is not None:
2260                print_str += keymsg.dpstr(maskmsg, more)
2261
2262            stats = self.get_attr("OVS_FLOW_ATTR_STATS")
2263            if stats is None:
2264                print_str += " packets:0, bytes:0,"
2265            else:
2266                print_str += " packets:%d, bytes:%d," % (
2267                    stats["packets"],
2268                    stats["bytes"],
2269                )
2270
2271            used = self.get_attr("OVS_FLOW_ATTR_USED")
2272            print_str += " used:"
2273            if used is None:
2274                print_str += "never,"
2275            else:
2276                used_time = int(used)
2277                cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC)
2278                used_time = (cur_time_sec * 1000) - used_time
2279                print_str += "{}s,".format(used_time / 1000)
2280
2281            print_str += " actions:"
2282            if (
2283                actsmsg is None
2284                or "attrs" not in actsmsg
2285                or len(actsmsg["attrs"]) == 0
2286            ):
2287                print_str += "drop"
2288            else:
2289                print_str += actsmsg.dpstr(more)
2290
2291            return print_str
2292
2293        def parse(self, flowstr, actstr, dpidx=0):
2294            OVS_UFID_F_OMIT_KEY = 1 << 0
2295            OVS_UFID_F_OMIT_MASK = 1 << 1
2296            OVS_UFID_F_OMIT_ACTIONS = 1 << 2
2297
2298            self["cmd"] = 0
2299            self["version"] = 0
2300            self["reserved"] = 0
2301            self["dpifindex"] = 0
2302
2303            if flowstr.startswith("ufid:"):
2304                count = 5
2305                while flowstr[count] != ",":
2306                    count += 1
2307                ufidstr = flowstr[5:count]
2308                flowstr = flowstr[count + 1 :]
2309            else:
2310                ufidstr = str(uuid.uuid4())
2311            uuidRawObj = uuid.UUID(ufidstr).fields
2312
2313            self["attrs"].append(
2314                [
2315                    "OVS_FLOW_ATTR_UFID",
2316                    [
2317                        uuidRawObj[0],
2318                        uuidRawObj[1] << 16 | uuidRawObj[2],
2319                        uuidRawObj[3] << 24
2320                        | uuidRawObj[4] << 16
2321                        | uuidRawObj[5] & (0xFF << 32) >> 32,
2322                        uuidRawObj[5] & (0xFFFFFFFF),
2323                    ],
2324                ]
2325            )
2326            self["attrs"].append(
2327                [
2328                    "OVS_FLOW_ATTR_UFID_FLAGS",
2329                    int(
2330                        OVS_UFID_F_OMIT_KEY
2331                        | OVS_UFID_F_OMIT_MASK
2332                        | OVS_UFID_F_OMIT_ACTIONS
2333                    ),
2334                ]
2335            )
2336
2337            k = ovskey()
2338            m = ovskey()
2339            k.parse(flowstr, m)
2340            self["attrs"].append(["OVS_FLOW_ATTR_KEY", k])
2341            self["attrs"].append(["OVS_FLOW_ATTR_MASK", m])
2342
2343            a = ovsactions()
2344            a.parse(actstr)
2345            self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a])
2346
2347    def __init__(self):
2348        GenericNetlinkSocket.__init__(self)
2349
2350        self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg)
2351
2352    def add_flow(self, dpifindex, flowmsg):
2353        """
2354        Send a new flow message to the kernel.
2355
2356        dpifindex should be a valid datapath obtained by calling
2357        into the OvsDatapath lookup
2358
2359        flowmsg is a flow object obtained by calling a dpparse
2360        """
2361
2362        flowmsg["cmd"] = OVS_FLOW_CMD_NEW
2363        flowmsg["version"] = OVS_DATAPATH_VERSION
2364        flowmsg["reserved"] = 0
2365        flowmsg["dpifindex"] = dpifindex
2366
2367        try:
2368            reply = self.nlm_request(
2369                flowmsg,
2370                msg_type=self.prid,
2371                msg_flags=NLM_F_REQUEST | NLM_F_ACK,
2372            )
2373            reply = reply[0]
2374        except NetlinkError as ne:
2375            print(flowmsg)
2376            raise ne
2377        return reply
2378
2379    def del_flows(self, dpifindex):
2380        """
2381        Send a del message to the kernel that will drop all flows.
2382
2383        dpifindex should be a valid datapath obtained by calling
2384        into the OvsDatapath lookup
2385        """
2386
2387        flowmsg = OvsFlow.ovs_flow_msg()
2388        flowmsg["cmd"] = OVS_FLOW_CMD_DEL
2389        flowmsg["version"] = OVS_DATAPATH_VERSION
2390        flowmsg["reserved"] = 0
2391        flowmsg["dpifindex"] = dpifindex
2392
2393        try:
2394            reply = self.nlm_request(
2395                flowmsg,
2396                msg_type=self.prid,
2397                msg_flags=NLM_F_REQUEST | NLM_F_ACK,
2398            )
2399            reply = reply[0]
2400        except NetlinkError as ne:
2401            print(flowmsg)
2402            raise ne
2403        return reply
2404
2405    def dump(self, dpifindex, flowspec=None):
2406        """
2407        Returns a list of messages containing flows.
2408
2409        dpifindex should be a valid datapath obtained by calling
2410        into the OvsDatapath lookup
2411
2412        flowpsec is a string which represents a flow in the dpctl
2413        format.
2414        """
2415        msg = OvsFlow.ovs_flow_msg()
2416
2417        msg["cmd"] = OVS_FLOW_CMD_GET
2418        msg["version"] = OVS_DATAPATH_VERSION
2419        msg["reserved"] = 0
2420        msg["dpifindex"] = dpifindex
2421
2422        msg_flags = NLM_F_REQUEST | NLM_F_ACK
2423        if flowspec is None:
2424            msg_flags |= NLM_F_DUMP
2425        rep = None
2426
2427        try:
2428            rep = self.nlm_request(
2429                msg,
2430                msg_type=self.prid,
2431                msg_flags=msg_flags,
2432            )
2433        except NetlinkError as ne:
2434            raise ne
2435        return rep
2436
2437    def miss(self, packetmsg):
2438        seq = packetmsg["header"]["sequence_number"]
2439        keystr = "(none)"
2440        key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY")
2441        if key_field is not None:
2442            keystr = key_field.dpstr(None, True)
2443
2444        pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET")
2445        pktpres = "yes" if pktdata is not None else "no"
2446
2447        print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True)
2448
2449    def execute(self, packetmsg):
2450        print("userspace execute command")
2451
2452    def action(self, packetmsg):
2453        print("userspace action command")
2454
2455
2456def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
2457    dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
2458    base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")
2459    megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")
2460    user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES")
2461    masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE")
2462
2463    print("%s:" % dp_name)
2464    print(
2465        "  lookups: hit:%d missed:%d lost:%d"
2466        % (base_stats["hit"], base_stats["missed"], base_stats["lost"])
2467    )
2468    print("  flows:%d" % base_stats["flows"])
2469    pkts = base_stats["hit"] + base_stats["missed"]
2470    avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0
2471    print(
2472        "  masks: hit:%d total:%d hit/pkt:%f"
2473        % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg)
2474    )
2475    print("  caches:")
2476    print("    masks-cache: size:%d" % masks_cache_size)
2477
2478    if user_features is not None:
2479        print("  features: 0x%X" % user_features)
2480
2481    # port print out
2482    for iface in ndb.interfaces:
2483        rep = vpl.info(iface.ifname, ifindex)
2484        if rep is not None:
2485            opts = ""
2486            vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS")
2487            if vpo:
2488                dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT")
2489                if dpo:
2490                    opts += " tnl-dport:%s" % socket.ntohs(dpo)
2491            print(
2492                "  port %d: %s (%s%s)"
2493                % (
2494                    rep.get_attr("OVS_VPORT_ATTR_PORT_NO"),
2495                    rep.get_attr("OVS_VPORT_ATTR_NAME"),
2496                    OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")),
2497                    opts,
2498                )
2499            )
2500
2501
2502def main(argv):
2503    nlmsg_atoms.ovskey = ovskey
2504    nlmsg_atoms.ovsactions = ovsactions
2505
2506    # version check for pyroute2
2507    prverscheck = pyroute2.__version__.split(".")
2508    if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6:
2509        print("Need to upgrade the python pyroute2 package to >= 0.6.")
2510        sys.exit(0)
2511
2512    parser = argparse.ArgumentParser()
2513    parser.add_argument(
2514        "-v",
2515        "--verbose",
2516        action="count",
2517        help="Increment 'verbose' output counter.",
2518        default=0,
2519    )
2520    subparsers = parser.add_subparsers()
2521
2522    showdpcmd = subparsers.add_parser("show")
2523    showdpcmd.add_argument(
2524        "showdp", metavar="N", type=str, nargs="?", help="Datapath Name"
2525    )
2526
2527    adddpcmd = subparsers.add_parser("add-dp")
2528    adddpcmd.add_argument("adddp", help="Datapath Name")
2529    adddpcmd.add_argument(
2530        "-u",
2531        "--upcall",
2532        action="store_true",
2533        help="Leave open a reader for upcalls",
2534    )
2535    adddpcmd.add_argument(
2536        "-V",
2537        "--versioning",
2538        required=False,
2539        help="Specify a custom version / feature string",
2540    )
2541
2542    deldpcmd = subparsers.add_parser("del-dp")
2543    deldpcmd.add_argument("deldp", help="Datapath Name")
2544
2545    addifcmd = subparsers.add_parser("add-if")
2546    addifcmd.add_argument("dpname", help="Datapath Name")
2547    addifcmd.add_argument("addif", help="Interface name for adding")
2548    addifcmd.add_argument(
2549        "-u",
2550        "--upcall",
2551        action="store_true",
2552        help="Leave open a reader for upcalls",
2553    )
2554    addifcmd.add_argument(
2555        "-t",
2556        "--ptype",
2557        type=str,
2558        default="netdev",
2559        choices=["netdev", "internal", "geneve", "vxlan"],
2560        help="Interface type (default netdev)",
2561    )
2562    addifcmd.add_argument(
2563        "-p",
2564        "--dport",
2565        type=int,
2566        default=0,
2567        help="Destination port (0 for default)"
2568    )
2569    addifcmd.add_argument(
2570        "-l",
2571        "--lwt",
2572        type=bool,
2573        default=True,
2574        help="Use LWT infrastructure instead of vport (default true)."
2575    )
2576    delifcmd = subparsers.add_parser("del-if")
2577    delifcmd.add_argument("dpname", help="Datapath Name")
2578    delifcmd.add_argument("delif", help="Interface name for adding")
2579    delifcmd.add_argument("-d",
2580                          "--dellink",
2581                          type=bool, default=False,
2582                          help="Delete the link as well.")
2583
2584    dumpflcmd = subparsers.add_parser("dump-flows")
2585    dumpflcmd.add_argument("dumpdp", help="Datapath Name")
2586
2587    addflcmd = subparsers.add_parser("add-flow")
2588    addflcmd.add_argument("flbr", help="Datapath name")
2589    addflcmd.add_argument("flow", help="Flow specification")
2590    addflcmd.add_argument("acts", help="Flow actions")
2591
2592    delfscmd = subparsers.add_parser("del-flows")
2593    delfscmd.add_argument("flsbr", help="Datapath name")
2594
2595    args = parser.parse_args()
2596
2597    if args.verbose > 0:
2598        if args.verbose > 1:
2599            logging.basicConfig(level=logging.DEBUG)
2600
2601    ovspk = OvsPacket()
2602    ovsdp = OvsDatapath()
2603    ovsvp = OvsVport(ovspk)
2604    ovsflow = OvsFlow()
2605    ndb = NDB()
2606
2607    sys.setrecursionlimit(100000)
2608
2609    if hasattr(args, "showdp"):
2610        found = False
2611        for iface in ndb.interfaces:
2612            rep = None
2613            if args.showdp is None:
2614                rep = ovsdp.info(iface.ifname, 0)
2615            elif args.showdp == iface.ifname:
2616                rep = ovsdp.info(iface.ifname, 0)
2617
2618            if rep is not None:
2619                found = True
2620                print_ovsdp_full(rep, iface.index, ndb, ovsvp)
2621
2622        if not found:
2623            msg = "No DP found"
2624            if args.showdp is not None:
2625                msg += ":'%s'" % args.showdp
2626            print(msg)
2627    elif hasattr(args, "adddp"):
2628        rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk)
2629        if rep is None:
2630            print("DP '%s' already exists" % args.adddp)
2631        else:
2632            print("DP '%s' added" % args.adddp)
2633        if args.upcall:
2634            ovspk.upcall_handler(ovsflow)
2635    elif hasattr(args, "deldp"):
2636        ovsdp.destroy(args.deldp)
2637    elif hasattr(args, "addif"):
2638        rep = ovsdp.info(args.dpname, 0)
2639        if rep is None:
2640            print("DP '%s' not found." % args.dpname)
2641            return 1
2642        dpindex = rep["dpifindex"]
2643        rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype,
2644                           args.dport, args.lwt)
2645        msg = "vport '%s'" % args.addif
2646        if rep and rep["header"]["error"] is None:
2647            msg += " added."
2648        else:
2649            msg += " failed to add."
2650        if args.upcall:
2651            if rep is None:
2652                rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk)
2653            ovsvp.upcall_handler(ovsflow)
2654    elif hasattr(args, "delif"):
2655        rep = ovsdp.info(args.dpname, 0)
2656        if rep is None:
2657            print("DP '%s' not found." % args.dpname)
2658            return 1
2659        rep = ovsvp.detach(rep["dpifindex"], args.delif)
2660        msg = "vport '%s'" % args.delif
2661        if rep and rep["header"]["error"] is None:
2662            msg += " removed."
2663        else:
2664            msg += " failed to remove."
2665        if args.dellink:
2666            ipr = pyroute2.iproute.IPRoute()
2667            ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0])
2668    elif hasattr(args, "dumpdp"):
2669        rep = ovsdp.info(args.dumpdp, 0)
2670        if rep is None:
2671            print("DP '%s' not found." % args.dumpdp)
2672            return 1
2673        rep = ovsflow.dump(rep["dpifindex"])
2674        for flow in rep:
2675            print(flow.dpstr(True if args.verbose > 0 else False))
2676    elif hasattr(args, "flbr"):
2677        rep = ovsdp.info(args.flbr, 0)
2678        if rep is None:
2679            print("DP '%s' not found." % args.flbr)
2680            return 1
2681        flow = OvsFlow.ovs_flow_msg()
2682        flow.parse(args.flow, args.acts, rep["dpifindex"])
2683        ovsflow.add_flow(rep["dpifindex"], flow)
2684    elif hasattr(args, "flsbr"):
2685        rep = ovsdp.info(args.flsbr, 0)
2686        if rep is None:
2687            print("DP '%s' not found." % args.flsbr)
2688        ovsflow.del_flows(rep["dpifindex"])
2689
2690    return 0
2691
2692
2693if __name__ == "__main__":
2694    sys.exit(main(sys.argv))
2695