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