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