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