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