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