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