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