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