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