brintos

brintos / linux-shallow public Read only

0
0
Text · 89.1 KiB · 8a0396b Raw
2776 lines · python
1#!/usr/bin/env python32# SPDX-License-Identifier: GPL-2.03 4# Controls the openvswitch module.  Part of the kselftest suite, but5# can be used for some diagnostic purpose as well.6 7import argparse8import errno9import ipaddress10import logging11import math12import multiprocessing13import re14import socket15import struct16import sys17import time18import types19import uuid20 21try:22    from pyroute2 import NDB23 24    from pyroute2.netlink import NLA_F_NESTED25    from pyroute2.netlink import NLM_F_ACK26    from pyroute2.netlink import NLM_F_DUMP27    from pyroute2.netlink import NLM_F_REQUEST28    from pyroute2.netlink import genlmsg29    from pyroute2.netlink import nla30    from pyroute2.netlink import nlmsg_atoms31    from pyroute2.netlink.event import EventSocket32    from pyroute2.netlink.exceptions import NetlinkError33    from pyroute2.netlink.generic import GenericNetlinkSocket34    from pyroute2.netlink.nlsocket import Marshal35    import pyroute236    import pyroute2.iproute37 38except ModuleNotFoundError:39    print("Need to install the python pyroute2 package >= 0.6.")40    sys.exit(1)41 42 43OVS_DATAPATH_FAMILY = "ovs_datapath"44OVS_VPORT_FAMILY = "ovs_vport"45OVS_FLOW_FAMILY = "ovs_flow"46OVS_PACKET_FAMILY = "ovs_packet"47OVS_METER_FAMILY = "ovs_meter"48OVS_CT_LIMIT_FAMILY = "ovs_ct_limit"49 50OVS_DATAPATH_VERSION = 251OVS_DP_CMD_NEW = 152OVS_DP_CMD_DEL = 253OVS_DP_CMD_GET = 354OVS_DP_CMD_SET = 455 56OVS_VPORT_CMD_NEW = 157OVS_VPORT_CMD_DEL = 258OVS_VPORT_CMD_GET = 359OVS_VPORT_CMD_SET = 460 61OVS_FLOW_CMD_NEW = 162OVS_FLOW_CMD_DEL = 263OVS_FLOW_CMD_GET = 364OVS_FLOW_CMD_SET = 465 66UINT32_MAX = 0xFFFFFFFF67 68def macstr(mac):69    outstr = ":".join(["%02X" % i for i in mac])70    return outstr71 72 73def strcspn(str1, str2):74    tot = 075    for char in str1:76        if str2.find(char) != -1:77            return tot78        tot += 179    return tot80 81 82def strspn(str1, str2):83    tot = 084    for char in str1:85        if str2.find(char) == -1:86            return tot87        tot += 188    return tot89 90 91def intparse(statestr, defmask="0xffffffff"):92    totalparse = strspn(statestr, "0123456789abcdefABCDEFx/")93    # scan until "/"94    count = strspn(statestr, "x0123456789abcdefABCDEF")95 96    firstnum = statestr[:count]97    if firstnum[-1] == "/":98        firstnum = firstnum[:-1]99    k = int(firstnum, 0)100 101    m = None102    if defmask is not None:103        secondnum = defmask104        if statestr[count] == "/":105            secondnum = statestr[count + 1 :]  # this is wrong...106        m = int(secondnum, 0)107 108    return statestr[totalparse + 1 :], k, m109 110 111def parse_flags(flag_str, flag_vals):112    bitResult = 0113    maskResult = 0114 115    if len(flag_str) == 0:116        return flag_str, bitResult, maskResult117 118    if flag_str[0].isdigit():119        idx = 0120        while flag_str[idx].isdigit() or flag_str[idx] == "x":121            idx += 1122        digits = flag_str[:idx]123        flag_str = flag_str[idx:]124 125        bitResult = int(digits, 0)126        maskResult = int(digits, 0)127 128    while len(flag_str) > 0 and (flag_str[0] == "+" or flag_str[0] == "-"):129        if flag_str[0] == "+":130            setFlag = True131        elif flag_str[0] == "-":132            setFlag = False133 134        flag_str = flag_str[1:]135 136        flag_len = 0137        while (138            flag_str[flag_len] != "+"139            and flag_str[flag_len] != "-"140            and flag_str[flag_len] != ","141            and flag_str[flag_len] != ")"142        ):143            flag_len += 1144 145        flag = flag_str[0:flag_len]146 147        if flag in flag_vals:148            if maskResult & flag_vals[flag]:149                raise KeyError(150                    "Flag %s set once, cannot be set in multiples" % flag151                )152 153            if setFlag:154                bitResult |= flag_vals[flag]155 156            maskResult |= flag_vals[flag]157        else:158            raise KeyError("Missing flag value: %s" % flag)159 160        flag_str = flag_str[flag_len:]161 162    return flag_str, bitResult, maskResult163 164 165def parse_ct_state(statestr):166    ct_flags = {167        "new": 1 << 0,168        "est": 1 << 1,169        "rel": 1 << 2,170        "rpl": 1 << 3,171        "inv": 1 << 4,172        "trk": 1 << 5,173        "snat": 1 << 6,174        "dnat": 1 << 7,175    }176 177    return parse_flags(statestr, ct_flags)178 179 180def convert_mac(data):181    def to_bytes(mac):182        mac_split = mac.split(":")183        ret = bytearray([int(i, 16) for i in mac_split])184        return bytes(ret)185 186    mac_str, _, mask_str = data.partition('/')187 188    if not mac_str:189        mac_str = mask_str = "00:00:00:00:00:00"190    elif not mask_str:191        mask_str = "FF:FF:FF:FF:FF:FF"192 193    return to_bytes(mac_str), to_bytes(mask_str)194 195def convert_ipv4(data):196    ip, _, mask = data.partition('/')197 198    if not ip:199        ip = mask = 0200    elif not mask:201        mask = 0xFFFFFFFF202    elif mask.isdigit():203        mask = (0xFFFFFFFF << (32 - int(mask))) & 0xFFFFFFFF204 205    return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask))206 207def convert_ipv6(data):208    ip, _, mask = data.partition('/')209 210    if not ip:211        ip = mask = 0212    elif not mask:213        mask = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'214    elif mask.isdigit():215        mask = ipaddress.IPv6Network("::/" + mask).hostmask216 217    return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed218 219def convert_int(size):220    def convert_int_sized(data):221        value, _, mask = data.partition('/')222 223        if not value:224            return 0, 0225        elif not mask:226            return int(value, 0), pow(2, size) - 1227        else:228            return int(value, 0), int(mask, 0)229 230    return convert_int_sized231 232def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False):233    if scanregex:234        m = re.search(scanstr, block_str)235        if m is None:236            if returnskipped:237                return block_str238            return False239        if returnskipped:240            block_str = block_str[len(m.group(0)) :]241            return block_str242        return True243 244    if block_str.startswith(scanstr):245        if returnskipped:246            block_str = block_str[len(scanstr) :]247        else:248            return True249 250    if returnskipped:251        return block_str252 253    return False254 255 256def parse_extract_field(257    block_str, fieldstr, scanfmt, convert, masked=False, defval=None258):259    if fieldstr and not block_str.startswith(fieldstr):260        return block_str, defval261 262    if fieldstr:263        str_skiplen = len(fieldstr)264        str_skipped = block_str[str_skiplen:]265        if str_skiplen == 0:266            return str_skipped, defval267    else:268        str_skiplen = 0269        str_skipped = block_str270 271    m = re.search(scanfmt, str_skipped)272    if m is None:273        raise ValueError("Bad fmt string")274 275    data = m.group(0)276    if convert:277        data = convert(m.group(0))278 279    str_skipped = str_skipped[len(m.group(0)) :]280    if masked:281        if str_skipped[0] == "/":282            raise ValueError("Masking support TBD...")283 284    str_skipped = str_skipped[strspn(str_skipped, ", ") :]285    return str_skipped, data286 287 288def parse_attrs(actstr, attr_desc):289    """Parses the given action string and returns a list of netlink290    attributes based on a list of attribute descriptions.291 292    Each element in the attribute description list is a tuple such as:293        (name, attr_name, parse_func)294    where:295        name: is the string representing the attribute296        attr_name: is the name of the attribute as defined in the uAPI.297        parse_func: is a callable accepting a string and returning either298            a single object (the parsed attribute value) or a tuple of299            two values (the parsed attribute value and the remaining string)300 301    Returns a list of attributes and the remaining string.302    """303    def parse_attr(actstr, key, func):304        actstr = actstr[len(key) :]305 306        if not func:307            return None, actstr308 309        delim = actstr[0]310        actstr = actstr[1:]311 312        if delim == "=":313            pos = strcspn(actstr, ",)")314            ret = func(actstr[:pos])315        else:316            ret = func(actstr)317 318        if isinstance(ret, tuple):319            (datum, actstr) = ret320        else:321            datum = ret322            actstr = actstr[strcspn(actstr, ",)"):]323 324        if delim == "(":325            if not actstr or actstr[0] != ")":326                raise ValueError("Action contains unbalanced parentheses")327 328            actstr = actstr[1:]329 330        actstr = actstr[strspn(actstr, ", ") :]331 332        return datum, actstr333 334    attrs = []335    attr_desc = list(attr_desc)336    while actstr and actstr[0] != ")" and attr_desc:337        found = False338        for i, (key, attr, func) in enumerate(attr_desc):339            if actstr.startswith(key):340                datum, actstr = parse_attr(actstr, key, func)341                attrs.append([attr, datum])342                found = True343                del attr_desc[i]344 345        if not found:346            raise ValueError("Unknown attribute: '%s'" % actstr)347 348        actstr = actstr[strspn(actstr, ", ") :]349 350    if actstr[0] != ")":351        raise ValueError("Action string contains extra garbage or has "352                         "unbalanced parenthesis: '%s'" % actstr)353 354    return attrs, actstr[1:]355 356 357class ovs_dp_msg(genlmsg):358    # include the OVS version359    # We need a custom header rather than just being able to rely on360    # genlmsg because fields ends up not expressing everything correctly361    # if we use the canonical example of setting fields = (('customfield',),)362    fields = genlmsg.fields + (("dpifindex", "I"),)363 364 365class ovsactions(nla):366    nla_flags = NLA_F_NESTED367 368    nla_map = (369        ("OVS_ACTION_ATTR_UNSPEC", "none"),370        ("OVS_ACTION_ATTR_OUTPUT", "uint32"),371        ("OVS_ACTION_ATTR_USERSPACE", "userspace"),372        ("OVS_ACTION_ATTR_SET", "ovskey"),373        ("OVS_ACTION_ATTR_PUSH_VLAN", "none"),374        ("OVS_ACTION_ATTR_POP_VLAN", "flag"),375        ("OVS_ACTION_ATTR_SAMPLE", "sample"),376        ("OVS_ACTION_ATTR_RECIRC", "uint32"),377        ("OVS_ACTION_ATTR_HASH", "none"),378        ("OVS_ACTION_ATTR_PUSH_MPLS", "none"),379        ("OVS_ACTION_ATTR_POP_MPLS", "flag"),380        ("OVS_ACTION_ATTR_SET_MASKED", "ovskey"),381        ("OVS_ACTION_ATTR_CT", "ctact"),382        ("OVS_ACTION_ATTR_TRUNC", "uint32"),383        ("OVS_ACTION_ATTR_PUSH_ETH", "none"),384        ("OVS_ACTION_ATTR_POP_ETH", "flag"),385        ("OVS_ACTION_ATTR_CT_CLEAR", "flag"),386        ("OVS_ACTION_ATTR_PUSH_NSH", "none"),387        ("OVS_ACTION_ATTR_POP_NSH", "flag"),388        ("OVS_ACTION_ATTR_METER", "none"),389        ("OVS_ACTION_ATTR_CLONE", "recursive"),390        ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"),391        ("OVS_ACTION_ATTR_ADD_MPLS", "none"),392        ("OVS_ACTION_ATTR_DEC_TTL", "none"),393        ("OVS_ACTION_ATTR_DROP", "uint32"),394        ("OVS_ACTION_ATTR_PSAMPLE", "psample"),395    )396 397    class psample(nla):398        nla_flags = NLA_F_NESTED399 400        nla_map = (401            ("OVS_PSAMPLE_ATTR_UNSPEC", "none"),402            ("OVS_PSAMPLE_ATTR_GROUP", "uint32"),403            ("OVS_PSAMPLE_ATTR_COOKIE", "array(uint8)"),404        )405 406        def dpstr(self, more=False):407            args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP")408 409            cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE")410            if cookie:411                args += ",cookie(%s)" % \412                        "".join(format(x, "02x") for x in cookie)413 414            return "psample(%s)" % args415 416        def parse(self, actstr):417            desc = (418                ("group", "OVS_PSAMPLE_ATTR_GROUP", int),419                ("cookie", "OVS_PSAMPLE_ATTR_COOKIE",420                    lambda x: list(bytearray.fromhex(x)))421            )422 423            attrs, actstr = parse_attrs(actstr, desc)424 425            for attr in attrs:426                self["attrs"].append(attr)427 428            return actstr429 430    class sample(nla):431        nla_flags = NLA_F_NESTED432 433        nla_map = (434            ("OVS_SAMPLE_ATTR_UNSPEC", "none"),435            ("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"),436            ("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"),437        )438 439        def dpstr(self, more=False):440            args = []441 442            args.append("sample={:.2f}%".format(443                100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") /444                UINT32_MAX))445 446            actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS")447            if actions:448                args.append("actions(%s)" % actions.dpstr(more))449 450            return "sample(%s)" % ",".join(args)451 452        def parse(self, actstr):453            def parse_nested_actions(actstr):454                subacts = ovsactions()455                parsed_len = subacts.parse(actstr)456                return subacts, actstr[parsed_len :]457 458            def percent_to_rate(percent):459                percent = float(percent.strip('%'))460                return int(math.floor(UINT32_MAX * (percent / 100.0) + .5))461 462            desc = (463                ("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate),464                ("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions),465            )466            attrs, actstr = parse_attrs(actstr, desc)467 468            for attr in attrs:469                self["attrs"].append(attr)470 471            return actstr472 473    class ctact(nla):474        nla_flags = NLA_F_NESTED475 476        nla_map = (477            ("OVS_CT_ATTR_NONE", "none"),478            ("OVS_CT_ATTR_COMMIT", "flag"),479            ("OVS_CT_ATTR_ZONE", "uint16"),480            ("OVS_CT_ATTR_MARK", "none"),481            ("OVS_CT_ATTR_LABELS", "none"),482            ("OVS_CT_ATTR_HELPER", "asciiz"),483            ("OVS_CT_ATTR_NAT", "natattr"),484            ("OVS_CT_ATTR_FORCE_COMMIT", "flag"),485            ("OVS_CT_ATTR_EVENTMASK", "uint32"),486            ("OVS_CT_ATTR_TIMEOUT", "asciiz"),487        )488 489        class natattr(nla):490            nla_flags = NLA_F_NESTED491 492            nla_map = (493                ("OVS_NAT_ATTR_NONE", "none"),494                ("OVS_NAT_ATTR_SRC", "flag"),495                ("OVS_NAT_ATTR_DST", "flag"),496                ("OVS_NAT_ATTR_IP_MIN", "ipaddr"),497                ("OVS_NAT_ATTR_IP_MAX", "ipaddr"),498                ("OVS_NAT_ATTR_PROTO_MIN", "uint16"),499                ("OVS_NAT_ATTR_PROTO_MAX", "uint16"),500                ("OVS_NAT_ATTR_PERSISTENT", "flag"),501                ("OVS_NAT_ATTR_PROTO_HASH", "flag"),502                ("OVS_NAT_ATTR_PROTO_RANDOM", "flag"),503            )504 505            def dpstr(self, more=False):506                print_str = "nat("507 508                if self.get_attr("OVS_NAT_ATTR_SRC"):509                    print_str += "src"510                elif self.get_attr("OVS_NAT_ATTR_DST"):511                    print_str += "dst"512                else:513                    print_str += "XXX-unknown-nat"514 515                if self.get_attr("OVS_NAT_ATTR_IP_MIN") or self.get_attr(516                    "OVS_NAT_ATTR_IP_MAX"517                ):518                    if self.get_attr("OVS_NAT_ATTR_IP_MIN"):519                        print_str += "=%s," % str(520                            self.get_attr("OVS_NAT_ATTR_IP_MIN")521                        )522 523                    if self.get_attr("OVS_NAT_ATTR_IP_MAX"):524                        print_str += "-%s," % str(525                            self.get_attr("OVS_NAT_ATTR_IP_MAX")526                        )527                else:528                    print_str += ","529 530                if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"):531                    print_str += "proto_min=%d," % self.get_attr(532                        "OVS_NAT_ATTR_PROTO_MIN"533                    )534 535                if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"):536                    print_str += "proto_max=%d," % self.get_attr(537                        "OVS_NAT_ATTR_PROTO_MAX"538                    )539 540                if self.get_attr("OVS_NAT_ATTR_PERSISTENT"):541                    print_str += "persistent,"542                if self.get_attr("OVS_NAT_ATTR_HASH"):543                    print_str += "hash,"544                if self.get_attr("OVS_NAT_ATTR_RANDOM"):545                    print_str += "random"546                print_str += ")"547                return print_str548 549        def dpstr(self, more=False):550            print_str = "ct("551 552            if self.get_attr("OVS_CT_ATTR_COMMIT") is not None:553                print_str += "commit,"554            if self.get_attr("OVS_CT_ATTR_ZONE") is not None:555                print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE")556            if self.get_attr("OVS_CT_ATTR_HELPER") is not None:557                print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER")558            if self.get_attr("OVS_CT_ATTR_NAT") is not None:559                print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more)560                print_str += ","561            if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None:562                print_str += "force,"563            if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None:564                print_str += "emask=0x%X," % self.get_attr(565                    "OVS_CT_ATTR_EVENTMASK"566                )567            if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None:568                print_str += "timeout=%s" % self.get_attr(569                    "OVS_CT_ATTR_TIMEOUT"570                )571            print_str += ")"572            return print_str573 574    class userspace(nla):575        nla_flags = NLA_F_NESTED576 577        nla_map = (578            ("OVS_USERSPACE_ATTR_UNUSED", "none"),579            ("OVS_USERSPACE_ATTR_PID", "uint32"),580            ("OVS_USERSPACE_ATTR_USERDATA", "array(uint8)"),581            ("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", "uint32"),582        )583 584        def dpstr(self, more=False):585            print_str = "userspace("586            if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None:587                print_str += "pid=%d," % self.get_attr(588                    "OVS_USERSPACE_ATTR_PID"589                )590            if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None:591                print_str += "userdata="592                for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"):593                    print_str += "%x." % f594            if self.get_attr("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT") is not None:595                print_str += "egress_tun_port=%d" % self.get_attr(596                    "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT"597                )598            print_str += ")"599            return print_str600 601        def parse(self, actstr):602            attrs_desc = (603                ("pid", "OVS_USERSPACE_ATTR_PID", int),604                ("userdata", "OVS_USERSPACE_ATTR_USERDATA",605                    lambda x: list(bytearray.fromhex(x))),606                ("egress_tun_port", "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", int)607            )608 609            attrs, actstr = parse_attrs(actstr, attrs_desc)610            for attr in attrs:611                self["attrs"].append(attr)612 613            return actstr614 615    def dpstr(self, more=False):616        print_str = ""617 618        for field in self["attrs"]:619            if field[1] == "none" or self.get_attr(field[0]) is None:620                continue621            if print_str != "":622                print_str += ","623 624            if field[0] == "OVS_ACTION_ATTR_OUTPUT":625                print_str += "%d" % int(self.get_attr(field[0]))626            elif field[0] == "OVS_ACTION_ATTR_RECIRC":627                print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))628            elif field[0] == "OVS_ACTION_ATTR_TRUNC":629                print_str += "trunc(%d)" % int(self.get_attr(field[0]))630            elif field[0] == "OVS_ACTION_ATTR_DROP":631                print_str += "drop(%d)" % int(self.get_attr(field[0]))632            elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR":633                print_str += "ct_clear"634            elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":635                print_str += "pop_vlan"636            elif field[0] == "OVS_ACTION_ATTR_POP_ETH":637                print_str += "pop_eth"638            elif field[0] == "OVS_ACTION_ATTR_POP_NSH":639                print_str += "pop_nsh"640            elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":641                print_str += "pop_mpls"642            else:643                datum = self.get_attr(field[0])644                if field[0] == "OVS_ACTION_ATTR_CLONE":645                    print_str += "clone("646                    print_str += datum.dpstr(more)647                    print_str += ")"648                elif field[0] == "OVS_ACTION_ATTR_SET" or \649                     field[0] == "OVS_ACTION_ATTR_SET_MASKED":650                    print_str += "set"651                    field = datum652                    mask = None653                    if field[0] == "OVS_ACTION_ATTR_SET_MASKED":654                        print_str += "_masked"655                        field = datum[0]656                        mask = datum[1]657                    print_str += "("658                    print_str += field.dpstr(mask, more)659                    print_str += ")"660                else:661                    try:662                        print_str += datum.dpstr(more)663                    except:664                        print_str += "{ATTR: %s not decoded}" % field[0]665 666        return print_str667 668    def parse(self, actstr):669        totallen = len(actstr)670        while len(actstr) != 0:671            parsed = False672            parencount = 0673            if actstr.startswith("drop"):674                # If no reason is provided, the implicit drop is used (i.e no675                # action). If some reason is given, an explicit action is used.676                reason = None677                if actstr.startswith("drop("):678                    parencount += 1679 680                    actstr, reason = parse_extract_field(681                        actstr,682                        "drop(",683                        r"([0-9]+)",684                        lambda x: int(x, 0),685                        False,686                        None,687                    )688 689                if reason is not None:690                    self["attrs"].append(["OVS_ACTION_ATTR_DROP", reason])691                    parsed = True692                else:693                    actstr = actstr[len("drop"): ]694                    return (totallen - len(actstr))695 696            elif parse_starts_block(actstr, r"^(\d+)", False, True):697                actstr, output = parse_extract_field(698                    actstr, None, r"(\d+)", lambda x: int(x), False, "0"699                )700                self["attrs"].append(["OVS_ACTION_ATTR_OUTPUT", output])701                parsed = True702            elif parse_starts_block(actstr, "recirc(", False):703                actstr, recircid = parse_extract_field(704                    actstr,705                    "recirc(",706                    r"([0-9a-fA-Fx]+)",707                    lambda x: int(x, 0),708                    False,709                    0,710                )711                parencount += 1712                self["attrs"].append(["OVS_ACTION_ATTR_RECIRC", recircid])713                parsed = True714 715            parse_flat_map = (716                ("ct_clear", "OVS_ACTION_ATTR_CT_CLEAR"),717                ("pop_vlan", "OVS_ACTION_ATTR_POP_VLAN"),718                ("pop_eth", "OVS_ACTION_ATTR_POP_ETH"),719                ("pop_nsh", "OVS_ACTION_ATTR_POP_NSH"),720            )721 722            for flat_act in parse_flat_map:723                if parse_starts_block(actstr, flat_act[0], False):724                    actstr = actstr[len(flat_act[0]):]725                    self["attrs"].append([flat_act[1], True])726                    actstr = actstr[strspn(actstr, ", ") :]727                    parsed = True728 729            if parse_starts_block(actstr, "clone(", False):730                parencount += 1731                subacts = ovsactions()732                actstr = actstr[len("clone("):]733                parsedLen = subacts.parse(actstr)734                lst = []735                self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts))736                actstr = actstr[parsedLen:]737                parsed = True738            elif parse_starts_block(actstr, "set(", False):739                parencount += 1740                k = ovskey()741                actstr = actstr[len("set("):]742                actstr = k.parse(actstr, None)743                self["attrs"].append(("OVS_ACTION_ATTR_SET", k))744                if not actstr.startswith(")"):745                    actstr = ")" + actstr746                parsed = True747            elif parse_starts_block(actstr, "set_masked(", False):748                parencount += 1749                k = ovskey()750                m = ovskey()751                actstr = actstr[len("set_masked("):]752                actstr = k.parse(actstr, m)753                self["attrs"].append(("OVS_ACTION_ATTR_SET_MASKED", [k, m]))754                if not actstr.startswith(")"):755                    actstr = ")" + actstr756                parsed = True757            elif parse_starts_block(actstr, "ct(", False):758                parencount += 1759                actstr = actstr[len("ct(") :]760                ctact = ovsactions.ctact()761 762                for scan in (763                    ("commit", "OVS_CT_ATTR_COMMIT", None),764                    ("force_commit", "OVS_CT_ATTR_FORCE_COMMIT", None),765                    ("zone", "OVS_CT_ATTR_ZONE", int),766                    ("mark", "OVS_CT_ATTR_MARK", int),767                    ("helper", "OVS_CT_ATTR_HELPER", lambda x, y: str(x)),768                    ("timeout", "OVS_CT_ATTR_TIMEOUT", lambda x, y: str(x)),769                ):770                    if actstr.startswith(scan[0]):771                        actstr = actstr[len(scan[0]) :]772                        if scan[2] is not None:773                            if actstr[0] != "=":774                                raise ValueError("Invalid ct attr")775                            actstr = actstr[1:]776                            pos = strcspn(actstr, ",)")777                            datum = scan[2](actstr[:pos], 0)778                            ctact["attrs"].append([scan[1], datum])779                            actstr = actstr[pos:]780                        else:781                            ctact["attrs"].append([scan[1], None])782                        actstr = actstr[strspn(actstr, ", ") :]783                    # it seems strange to put this here, but nat() is a complex784                    # sub-action and this lets it sit anywhere in the ct() action785                    if actstr.startswith("nat"):786                        actstr = actstr[3:]787                        natact = ovsactions.ctact.natattr()788 789                        if actstr.startswith("("):790                            parencount += 1791                            t = None792                            actstr = actstr[1:]793                            if actstr.startswith("src"):794                                t = "OVS_NAT_ATTR_SRC"795                                actstr = actstr[3:]796                            elif actstr.startswith("dst"):797                                t = "OVS_NAT_ATTR_DST"798                                actstr = actstr[3:]799 800                            actstr, ip_block_min = parse_extract_field(801                                actstr, "=", r"([0-9a-fA-F\.]+)", str, False802                            )803                            actstr, ip_block_max = parse_extract_field(804                                actstr, "-", r"([0-9a-fA-F\.]+)", str, False805                            )806 807                            actstr, proto_min = parse_extract_field(808                                actstr, ":", r"(\d+)", int, False809                            )810                            actstr, proto_max = parse_extract_field(811                                actstr, "-", r"(\d+)", int, False812                            )813 814                            if t is not None:815                                natact["attrs"].append([t, None])816 817                                if ip_block_min is not None:818                                    natact["attrs"].append(819                                        ["OVS_NAT_ATTR_IP_MIN", ip_block_min]820                                    )821                                if ip_block_max is not None:822                                    natact["attrs"].append(823                                        ["OVS_NAT_ATTR_IP_MAX", ip_block_max]824                                    )825                                if proto_min is not None:826                                    natact["attrs"].append(827                                        ["OVS_NAT_ATTR_PROTO_MIN", proto_min]828                                    )829                                if proto_max is not None:830                                    natact["attrs"].append(831                                        ["OVS_NAT_ATTR_PROTO_MAX", proto_max]832                                    )833 834                            for natscan in (835                                ("persistent", "OVS_NAT_ATTR_PERSISTENT"),836                                ("hash", "OVS_NAT_ATTR_PROTO_HASH"),837                                ("random", "OVS_NAT_ATTR_PROTO_RANDOM"),838                            ):839                                if actstr.startswith(natscan[0]):840                                    actstr = actstr[len(natscan[0]) :]841                                    natact["attrs"].append([natscan[1], None])842                                    actstr = actstr[strspn(actstr, ", ") :]843 844                        ctact["attrs"].append(["OVS_CT_ATTR_NAT", natact])845                        actstr = actstr[strspn(actstr, ", ") :]846 847                self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact])848                parsed = True849 850            elif parse_starts_block(actstr, "sample(", False):851                sampleact = self.sample()852                actstr = sampleact.parse(actstr[len("sample(") : ])853                self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact])854                parsed = True855 856            elif parse_starts_block(actstr, "psample(", False):857                psampleact = self.psample()858                actstr = psampleact.parse(actstr[len("psample(") : ])859                self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact])860                parsed = True861 862            elif parse_starts_block(actstr, "userspace(", False):863                uact = self.userspace()864                actstr = uact.parse(actstr[len("userspace(") : ])865                self["attrs"].append(["OVS_ACTION_ATTR_USERSPACE", uact])866                parsed = True867 868            elif parse_starts_block(actstr, "trunc(", False):869                parencount += 1870                actstr, val = parse_extract_field(871                    actstr,872                    "trunc(",873                    r"([0-9]+)",874                    int,875                    False,876                    None,877                )878                self["attrs"].append(["OVS_ACTION_ATTR_TRUNC", val])879                parsed = True880 881            actstr = actstr[strspn(actstr, ", ") :]882            while parencount > 0:883                parencount -= 1884                actstr = actstr[strspn(actstr, " "):]885                if len(actstr) and actstr[0] != ")":886                    raise ValueError("Action str: '%s' unbalanced" % actstr)887                actstr = actstr[1:]888 889            if len(actstr) and actstr[0] == ")":890                return (totallen - len(actstr))891 892            actstr = actstr[strspn(actstr, ", ") :]893 894            if not parsed:895                raise ValueError("Action str: '%s' not supported" % actstr)896 897        return (totallen - len(actstr))898 899 900class ovskey(nla):901    nla_flags = NLA_F_NESTED902    nla_map = (903        ("OVS_KEY_ATTR_UNSPEC", "none"),904        ("OVS_KEY_ATTR_ENCAP", "none"),905        ("OVS_KEY_ATTR_PRIORITY", "uint32"),906        ("OVS_KEY_ATTR_IN_PORT", "uint32"),907        ("OVS_KEY_ATTR_ETHERNET", "ethaddr"),908        ("OVS_KEY_ATTR_VLAN", "uint16"),909        ("OVS_KEY_ATTR_ETHERTYPE", "be16"),910        ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"),911        ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"),912        ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"),913        ("OVS_KEY_ATTR_UDP", "ovs_key_udp"),914        ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"),915        ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"),916        ("OVS_KEY_ATTR_ARP", "ovs_key_arp"),917        ("OVS_KEY_ATTR_ND", "ovs_key_nd"),918        ("OVS_KEY_ATTR_SKB_MARK", "uint32"),919        ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"),920        ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"),921        ("OVS_KEY_ATTR_TCP_FLAGS", "be16"),922        ("OVS_KEY_ATTR_DP_HASH", "uint32"),923        ("OVS_KEY_ATTR_RECIRC_ID", "uint32"),924        ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"),925        ("OVS_KEY_ATTR_CT_STATE", "uint32"),926        ("OVS_KEY_ATTR_CT_ZONE", "uint16"),927        ("OVS_KEY_ATTR_CT_MARK", "uint32"),928        ("OVS_KEY_ATTR_CT_LABELS", "none"),929        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"),930        ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"),931        ("OVS_KEY_ATTR_NSH", "none"),932        ("OVS_KEY_ATTR_PACKET_TYPE", "none"),933        ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"),934        ("OVS_KEY_ATTR_TUNNEL_INFO", "none"),935        ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"),936    )937 938    class ovs_key_proto(nla):939        fields = (940            ("src", "!H"),941            ("dst", "!H"),942        )943 944        fields_map = (945            ("src", "src", "%d", lambda x: int(x) if x else 0,946                convert_int(16)),947            ("dst", "dst", "%d", lambda x: int(x) if x else 0,948                convert_int(16)),949        )950 951        def __init__(952            self,953            protostr,954            data=None,955            offset=None,956            parent=None,957            length=None,958            init=None,959        ):960            self.proto_str = protostr961            nla.__init__(962                self,963                data=data,964                offset=offset,965                parent=parent,966                length=length,967                init=init,968            )969 970        def parse(self, flowstr, typeInst):971            if not flowstr.startswith(self.proto_str):972                return None, None973 974            k = typeInst()975            m = typeInst()976 977            flowstr = flowstr[len(self.proto_str) :]978            if flowstr.startswith("("):979                flowstr = flowstr[1:]980 981            keybits = b""982            maskbits = b""983            for f in self.fields_map:984                if flowstr.startswith(f[1]):985                    # the following assumes that the field looks986                    # something like 'field.' where '.' is a987                    # character that we don't exactly care about.988                    flowstr = flowstr[len(f[1]) + 1 :]989                    splitchar = 0990                    for c in flowstr:991                        if c == "," or c == ")":992                            break993                        splitchar += 1994                    data = flowstr[:splitchar]995                    flowstr = flowstr[splitchar:]996                else:997                    data = ""998 999                if len(f) > 4:1000                    k[f[0]], m[f[0]] = f[4](data)1001                else:1002                    k[f[0]] = f[3](data)1003                    m[f[0]] = f[3](data)1004 1005                flowstr = flowstr[strspn(flowstr, ", ") :]1006                if len(flowstr) == 0:1007                    return flowstr, k, m1008 1009            flowstr = flowstr[strspn(flowstr, "), ") :]1010 1011            return flowstr, k, m1012 1013        def dpstr(self, masked=None, more=False):1014            outstr = self.proto_str + "("1015            first = False1016            for f in self.fields_map:1017                if first:1018                    outstr += ","1019                if masked is None:1020                    outstr += "%s=" % f[0]1021                    if isinstance(f[2], str):1022                        outstr += f[2] % self[f[1]]1023                    else:1024                        outstr += f[2](self[f[1]])1025                    first = True1026                elif more or f[3](masked[f[1]]) != 0:1027                    outstr += "%s=" % f[0]1028                    if isinstance(f[2], str):1029                        outstr += f[2] % self[f[1]]1030                    else:1031                        outstr += f[2](self[f[1]])1032                    outstr += "/"1033                    if isinstance(f[2], str):1034                        outstr += f[2] % masked[f[1]]1035                    else:1036                        outstr += f[2](masked[f[1]])1037                    first = True1038            outstr += ")"1039            return outstr1040 1041    class ethaddr(ovs_key_proto):1042        fields = (1043            ("src", "!6s"),1044            ("dst", "!6s"),1045        )1046 1047        fields_map = (1048            (1049                "src",1050                "src",1051                macstr,1052                lambda x: int.from_bytes(x, "big"),1053                convert_mac,1054            ),1055            (1056                "dst",1057                "dst",1058                macstr,1059                lambda x: int.from_bytes(x, "big"),1060                convert_mac,1061            ),1062        )1063 1064        def __init__(1065            self,1066            data=None,1067            offset=None,1068            parent=None,1069            length=None,1070            init=None,1071        ):1072            ovskey.ovs_key_proto.__init__(1073                self,1074                "eth",1075                data=data,1076                offset=offset,1077                parent=parent,1078                length=length,1079                init=init,1080            )1081 1082    class ovs_key_ipv4(ovs_key_proto):1083        fields = (1084            ("src", "!I"),1085            ("dst", "!I"),1086            ("proto", "B"),1087            ("tos", "B"),1088            ("ttl", "B"),1089            ("frag", "B"),1090        )1091 1092        fields_map = (1093            (1094                "src",1095                "src",1096                lambda x: str(ipaddress.IPv4Address(x)),1097                int,1098                convert_ipv4,1099            ),1100            (1101                "dst",1102                "dst",1103                lambda x: str(ipaddress.IPv4Address(x)),1104                int,1105                convert_ipv4,1106            ),1107            ("proto", "proto", "%d", lambda x: int(x) if x else 0,1108                convert_int(8)),1109            ("tos", "tos", "%d", lambda x: int(x) if x else 0,1110                convert_int(8)),1111            ("ttl", "ttl", "%d", lambda x: int(x) if x else 0,1112                convert_int(8)),1113            ("frag", "frag", "%d", lambda x: int(x) if x else 0,1114                convert_int(8)),1115        )1116 1117        def __init__(1118            self,1119            data=None,1120            offset=None,1121            parent=None,1122            length=None,1123            init=None,1124        ):1125            ovskey.ovs_key_proto.__init__(1126                self,1127                "ipv4",1128                data=data,1129                offset=offset,1130                parent=parent,1131                length=length,1132                init=init,1133            )1134 1135    class ovs_key_ipv6(ovs_key_proto):1136        fields = (1137            ("src", "!16s"),1138            ("dst", "!16s"),1139            ("label", "!I"),1140            ("proto", "B"),1141            ("tclass", "B"),1142            ("hlimit", "B"),1143            ("frag", "B"),1144        )1145 1146        fields_map = (1147            (1148                "src",1149                "src",1150                lambda x: str(ipaddress.IPv6Address(x)),1151                lambda x: ipaddress.IPv6Address(x).packed if x else 0,1152                convert_ipv6,1153            ),1154            (1155                "dst",1156                "dst",1157                lambda x: str(ipaddress.IPv6Address(x)),1158                lambda x: ipaddress.IPv6Address(x).packed if x else 0,1159                convert_ipv6,1160            ),1161            ("label", "label", "%d", lambda x: int(x) if x else 0),1162            ("proto", "proto", "%d", lambda x: int(x) if x else 0),1163            ("tclass", "tclass", "%d", lambda x: int(x) if x else 0),1164            ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0),1165            ("frag", "frag", "%d", lambda x: int(x) if x else 0),1166        )1167 1168        def __init__(1169            self,1170            data=None,1171            offset=None,1172            parent=None,1173            length=None,1174            init=None,1175        ):1176            ovskey.ovs_key_proto.__init__(1177                self,1178                "ipv6",1179                data=data,1180                offset=offset,1181                parent=parent,1182                length=length,1183                init=init,1184            )1185 1186    class ovs_key_tcp(ovs_key_proto):1187        def __init__(1188            self,1189            data=None,1190            offset=None,1191            parent=None,1192            length=None,1193            init=None,1194        ):1195            ovskey.ovs_key_proto.__init__(1196                self,1197                "tcp",1198                data=data,1199                offset=offset,1200                parent=parent,1201                length=length,1202                init=init,1203            )1204 1205    class ovs_key_udp(ovs_key_proto):1206        def __init__(1207            self,1208            data=None,1209            offset=None,1210            parent=None,1211            length=None,1212            init=None,1213        ):1214            ovskey.ovs_key_proto.__init__(1215                self,1216                "udp",1217                data=data,1218                offset=offset,1219                parent=parent,1220                length=length,1221                init=init,1222            )1223 1224    class ovs_key_sctp(ovs_key_proto):1225        def __init__(1226            self,1227            data=None,1228            offset=None,1229            parent=None,1230            length=None,1231            init=None,1232        ):1233            ovskey.ovs_key_proto.__init__(1234                self,1235                "sctp",1236                data=data,1237                offset=offset,1238                parent=parent,1239                length=length,1240                init=init,1241            )1242 1243    class ovs_key_icmp(ovs_key_proto):1244        fields = (1245            ("type", "B"),1246            ("code", "B"),1247        )1248 1249        fields_map = (1250            ("type", "type", "%d", lambda x: int(x) if x else 0),1251            ("code", "code", "%d", lambda x: int(x) if x else 0),1252        )1253 1254        def __init__(1255            self,1256            data=None,1257            offset=None,1258            parent=None,1259            length=None,1260            init=None,1261        ):1262            ovskey.ovs_key_proto.__init__(1263                self,1264                "icmp",1265                data=data,1266                offset=offset,1267                parent=parent,1268                length=length,1269                init=init,1270            )1271 1272    class ovs_key_icmpv6(ovs_key_icmp):1273        def __init__(1274            self,1275            data=None,1276            offset=None,1277            parent=None,1278            length=None,1279            init=None,1280        ):1281            ovskey.ovs_key_proto.__init__(1282                self,1283                "icmpv6",1284                data=data,1285                offset=offset,1286                parent=parent,1287                length=length,1288                init=init,1289            )1290 1291    class ovs_key_arp(ovs_key_proto):1292        fields = (1293            ("sip", "!I"),1294            ("tip", "!I"),1295            ("op", "!H"),1296            ("sha", "!6s"),1297            ("tha", "!6s"),1298            ("pad", "xx"),1299        )1300 1301        fields_map = (1302            (1303                "sip",1304                "sip",1305                lambda x: str(ipaddress.IPv4Address(x)),1306                int,1307                convert_ipv4,1308            ),1309            (1310                "tip",1311                "tip",1312                lambda x: str(ipaddress.IPv4Address(x)),1313                int,1314                convert_ipv4,1315            ),1316            ("op", "op", "%d", lambda x: int(x) if x else 0),1317            (1318                "sha",1319                "sha",1320                macstr,1321                lambda x: int.from_bytes(x, "big"),1322                convert_mac,1323            ),1324            (1325                "tha",1326                "tha",1327                macstr,1328                lambda x: int.from_bytes(x, "big"),1329                convert_mac,1330            ),1331        )1332 1333        def __init__(1334            self,1335            data=None,1336            offset=None,1337            parent=None,1338            length=None,1339            init=None,1340        ):1341            ovskey.ovs_key_proto.__init__(1342                self,1343                "arp",1344                data=data,1345                offset=offset,1346                parent=parent,1347                length=length,1348                init=init,1349            )1350 1351    class ovs_key_nd(ovs_key_proto):1352        fields = (1353            ("target", "!16s"),1354            ("sll", "!6s"),1355            ("tll", "!6s"),1356        )1357 1358        fields_map = (1359            (1360                "target",1361                "target",1362                lambda x: str(ipaddress.IPv6Address(x)),1363                convert_ipv6,1364            ),1365            ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")),1366            ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")),1367        )1368 1369        def __init__(1370            self,1371            data=None,1372            offset=None,1373            parent=None,1374            length=None,1375            init=None,1376        ):1377            ovskey.ovs_key_proto.__init__(1378                self,1379                "nd",1380                data=data,1381                offset=offset,1382                parent=parent,1383                length=length,1384                init=init,1385            )1386 1387    class ovs_key_ct_tuple_ipv4(ovs_key_proto):1388        fields = (1389            ("src", "!I"),1390            ("dst", "!I"),1391            ("tp_src", "!H"),1392            ("tp_dst", "!H"),1393            ("proto", "B"),1394        )1395 1396        fields_map = (1397            (1398                "src",1399                "src",1400                lambda x: str(ipaddress.IPv4Address(x)),1401                int,1402                convert_ipv4,1403            ),1404            (1405                "dst",1406                "dst",1407                lambda x: str(ipaddress.IPv4Address(x)),1408                int,1409                convert_ipv4,1410            ),1411            ("tp_src", "tp_src", "%d", int),1412            ("tp_dst", "tp_dst", "%d", int),1413            ("proto", "proto", "%d", int),1414        )1415 1416        def __init__(1417            self,1418            data=None,1419            offset=None,1420            parent=None,1421            length=None,1422            init=None,1423        ):1424            ovskey.ovs_key_proto.__init__(1425                self,1426                "ct_tuple4",1427                data=data,1428                offset=offset,1429                parent=parent,1430                length=length,1431                init=init,1432            )1433 1434    class ovs_key_ct_tuple_ipv6(nla):1435        fields = (1436            ("src", "!16s"),1437            ("dst", "!16s"),1438            ("tp_src", "!H"),1439            ("tp_dst", "!H"),1440            ("proto", "B"),1441        )1442 1443        fields_map = (1444            (1445                "src",1446                "src",1447                lambda x: str(ipaddress.IPv6Address(x)),1448                convert_ipv6,1449            ),1450            (1451                "dst",1452                "dst",1453                lambda x: str(ipaddress.IPv6Address(x)),1454                convert_ipv6,1455            ),1456            ("tp_src", "tp_src", "%d", int),1457            ("tp_dst", "tp_dst", "%d", int),1458            ("proto", "proto", "%d", int),1459        )1460 1461        def __init__(1462            self,1463            data=None,1464            offset=None,1465            parent=None,1466            length=None,1467            init=None,1468        ):1469            ovskey.ovs_key_proto.__init__(1470                self,1471                "ct_tuple6",1472                data=data,1473                offset=offset,1474                parent=parent,1475                length=length,1476                init=init,1477            )1478 1479    class ovs_key_tunnel(nla):1480        nla_flags = NLA_F_NESTED1481 1482        nla_map = (1483            ("OVS_TUNNEL_KEY_ATTR_ID", "be64"),1484            ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"),1485            ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"),1486            ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"),1487            ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"),1488            ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"),1489            ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"),1490            ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"),1491            ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"),1492            ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"),1493            ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"),1494            ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"),1495            ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"),1496            ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"),1497            ("OVS_TUNNEL_KEY_ATTR_PAD", "none"),1498            ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"),1499            ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"),1500        )1501 1502        def parse(self, flowstr, mask=None):1503            if not flowstr.startswith("tunnel("):1504                return None, None1505 1506            k = ovskey.ovs_key_tunnel()1507            if mask is not None:1508                mask = ovskey.ovs_key_tunnel()1509 1510            flowstr = flowstr[len("tunnel("):]1511 1512            v6_address = None1513 1514            fields = [1515                ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID",1516                 0xffffffffffffffff, None, None),1517 1518                ("src=", r"([0-9a-fA-F\.]+)", str,1519                 "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0",1520                 False),1521                ("dst=", r"([0-9a-fA-F\.]+)", str,1522                 "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0",1523                 False),1524 1525                ("ipv6_src=", r"([0-9a-fA-F:]+)", str,1526                 "OVS_TUNNEL_KEY_ATTR_IPV6_SRC",1527                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),1528                ("ipv6_dst=", r"([0-9a-fA-F:]+)", str,1529                 "OVS_TUNNEL_KEY_ATTR_IPV6_DST",1530                 "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True),1531 1532                ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0,1533                 None),1534                ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0,1535                 None),1536 1537                ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC",1538                 65535, 0, None),1539                ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST",1540                 65535, 0, None),1541            ]1542 1543            forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"]1544 1545            for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields:1546                flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False)1547                if not attr_name:1548                    raise Exception("Bad list value in tunnel fields")1549 1550                if value is None and attr_name in forced_include:1551                    value = default_val1552                    mask_val = default_val1553 1554                if value is not None:1555                    if v46_flag is not None:1556                        if v6_address is None:1557                            v6_address = v46_flag1558                        if v46_flag != v6_address:1559                            raise ValueError("Cannot mix v6 and v4 addresses")1560                    k["attrs"].append([attr_name, value])1561                    if mask is not None:1562                        mask["attrs"].append([attr_name, mask_val])1563                else:1564                    if v46_flag is not None:1565                        if v6_address is None or v46_flag != v6_address:1566                            continue1567                    if mask is not None:1568                        mask["attrs"].append([attr_name, default_val])1569 1570            if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID":1571                raise ValueError("Needs a tunid set")1572 1573            if flowstr.startswith("flags("):1574                flowstr = flowstr[len("flags("):]1575                flagspos = flowstr.find(")")1576                flags = flowstr[:flagspos]1577                flowstr = flowstr[flagspos + 1:]1578 1579                flag_attrs = {1580                    "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT",1581                    "csum": "OVS_TUNNEL_KEY_ATTR_CSUM",1582                    "oam": "OVS_TUNNEL_KEY_ATTR_OAM"1583                }1584 1585                for flag in flags.split("|"):1586                    if flag in flag_attrs:1587                        k["attrs"].append([flag_attrs[flag], True])1588                        if mask is not None:1589                            mask["attrs"].append([flag_attrs[flag], True])1590 1591            flowstr = flowstr[strspn(flowstr, ", ") :]1592            return flowstr, k, mask1593 1594        def dpstr(self, mask=None, more=False):1595            print_str = "tunnel("1596 1597            flagsattrs = []1598            for k in self["attrs"]:1599                noprint = False1600                if k[0] == "OVS_TUNNEL_KEY_ATTR_ID":1601                    print_str += "tun_id=%d" % k[1]1602                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC":1603                    print_str += "src=%s" % k[1]1604                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST":1605                    print_str += "dst=%s" % k[1]1606                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC":1607                    print_str += "ipv6_src=%s" % k[1]1608                elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST":1609                    print_str += "ipv6_dst=%s" % k[1]1610                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS":1611                    print_str += "tos=%d" % k[1]1612                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL":1613                    print_str += "ttl=%d" % k[1]1614                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC":1615                    print_str += "tp_src=%d" % k[1]1616                elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST":1617                    print_str += "tp_dst=%d" % k[1]1618                elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT":1619                    noprint = True1620                    flagsattrs.append("df")1621                elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM":1622                    noprint = True1623                    flagsattrs.append("csum")1624                elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM":1625                    noprint = True1626                    flagsattrs.append("oam")1627 1628                if not noprint:1629                    print_str += ","1630 1631            if len(flagsattrs):1632                print_str += "flags(" + "|".join(flagsattrs) + ")"1633            print_str += ")"1634            return print_str1635 1636    class ovs_key_mpls(nla):1637        fields = (("lse", ">I"),)1638 1639    def parse(self, flowstr, mask=None):1640        for field in (1641            ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse),1642            ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse),1643            ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse),1644            ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel),1645            ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse),1646            ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state),1647            ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse),1648            ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse),1649            ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse),1650            (1651                "OVS_KEY_ATTR_ETHERNET",1652                "eth",1653                ovskey.ethaddr,1654            ),1655            (1656                "OVS_KEY_ATTR_ETHERTYPE",1657                "eth_type",1658                lambda x: intparse(x, "0xffff"),1659            ),1660            (1661                "OVS_KEY_ATTR_IPV4",1662                "ipv4",1663                ovskey.ovs_key_ipv4,1664            ),1665            (1666                "OVS_KEY_ATTR_IPV6",1667                "ipv6",1668                ovskey.ovs_key_ipv6,1669            ),1670            (1671                "OVS_KEY_ATTR_ARP",1672                "arp",1673                ovskey.ovs_key_arp,1674            ),1675            (1676                "OVS_KEY_ATTR_TCP",1677                "tcp",1678                ovskey.ovs_key_tcp,1679            ),1680            (1681                "OVS_KEY_ATTR_UDP",1682                "udp",1683                ovskey.ovs_key_udp,1684            ),1685            (1686                "OVS_KEY_ATTR_ICMP",1687                "icmp",1688                ovskey.ovs_key_icmp,1689            ),1690            (1691                "OVS_KEY_ATTR_TCP_FLAGS",1692                "tcp_flags",1693                lambda x: parse_flags(x, None),1694            ),1695        ):1696            fld = field[1] + "("1697            if not flowstr.startswith(fld):1698                continue1699 1700            if not isinstance(field[2], types.FunctionType):1701                nk = field[2]()1702                flowstr, k, m = nk.parse(flowstr, field[2])1703            else:1704                flowstr = flowstr[len(fld) :]1705                flowstr, k, m = field[2](flowstr)1706 1707            if m and mask is not None:1708                mask["attrs"].append([field[0], m])1709            self["attrs"].append([field[0], k])1710 1711            flowstr = flowstr[strspn(flowstr, "), ") :]1712 1713        return flowstr1714 1715    def dpstr(self, mask=None, more=False):1716        print_str = ""1717 1718        for field in (1719            (1720                "OVS_KEY_ATTR_PRIORITY",1721                "skb_priority",1722                "%d",1723                lambda x: False,1724                True,1725            ),1726            (1727                "OVS_KEY_ATTR_SKB_MARK",1728                "skb_mark",1729                "%d",1730                lambda x: False,1731                True,1732            ),1733            (1734                "OVS_KEY_ATTR_RECIRC_ID",1735                "recirc_id",1736                "0x%08X",1737                lambda x: False,1738                True,1739            ),1740            (1741                "OVS_KEY_ATTR_DP_HASH",1742                "dp_hash",1743                "0x%08X",1744                lambda x: False,1745                True,1746            ),1747            (1748                "OVS_KEY_ATTR_TUNNEL",1749                "tunnel",1750                None,1751                False,1752                False,1753            ),1754            (1755                "OVS_KEY_ATTR_CT_STATE",1756                "ct_state",1757                "0x%04x",1758                lambda x: False,1759                True,1760            ),1761            (1762                "OVS_KEY_ATTR_CT_ZONE",1763                "ct_zone",1764                "0x%04x",1765                lambda x: False,1766                True,1767            ),1768            (1769                "OVS_KEY_ATTR_CT_MARK",1770                "ct_mark",1771                "0x%08x",1772                lambda x: False,1773                True,1774            ),1775            (1776                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4",1777                None,1778                None,1779                False,1780                False,1781            ),1782            (1783                "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6",1784                None,1785                None,1786                False,1787                False,1788            ),1789            (1790                "OVS_KEY_ATTR_IN_PORT",1791                "in_port",1792                "%d",1793                lambda x: True,1794                True,1795            ),1796            ("OVS_KEY_ATTR_ETHERNET", None, None, False, False),1797            (1798                "OVS_KEY_ATTR_ETHERTYPE",1799                "eth_type",1800                "0x%04x",1801                lambda x: int(x) == 0xFFFF,1802                True,1803            ),1804            ("OVS_KEY_ATTR_IPV4", None, None, False, False),1805            ("OVS_KEY_ATTR_IPV6", None, None, False, False),1806            ("OVS_KEY_ATTR_ARP", None, None, False, False),1807            ("OVS_KEY_ATTR_TCP", None, None, False, False),1808            (1809                "OVS_KEY_ATTR_TCP_FLAGS",1810                "tcp_flags",1811                "0x%04x",1812                lambda x: False,1813                True,1814            ),1815            ("OVS_KEY_ATTR_UDP", None, None, False, False),1816            ("OVS_KEY_ATTR_SCTP", None, None, False, False),1817            ("OVS_KEY_ATTR_ICMP", None, None, False, False),1818            ("OVS_KEY_ATTR_ICMPV6", None, None, False, False),1819            ("OVS_KEY_ATTR_ND", None, None, False, False),1820        ):1821            v = self.get_attr(field[0])1822            if v is not None:1823                m = None if mask is None else mask.get_attr(field[0])1824                if field[4] is False:1825                    print_str += v.dpstr(m, more)1826                    print_str += ","1827                else:1828                    if m is None or field[3](m):1829                        print_str += field[1] + "("1830                        print_str += field[2] % v1831                        print_str += "),"1832                    elif more or m != 0:1833                        print_str += field[1] + "("1834                        print_str += (field[2] % v) + "/" + (field[2] % m)1835                        print_str += "),"1836 1837        return print_str1838 1839 1840class OvsPacket(GenericNetlinkSocket):1841    OVS_PACKET_CMD_MISS = 1  # Flow table miss1842    OVS_PACKET_CMD_ACTION = 2  # USERSPACE action1843    OVS_PACKET_CMD_EXECUTE = 3  # Apply actions to packet1844 1845    class ovs_packet_msg(ovs_dp_msg):1846        nla_map = (1847            ("OVS_PACKET_ATTR_UNSPEC", "none"),1848            ("OVS_PACKET_ATTR_PACKET", "array(uint8)"),1849            ("OVS_PACKET_ATTR_KEY", "ovskey"),1850            ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"),1851            ("OVS_PACKET_ATTR_USERDATA", "none"),1852            ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"),1853            ("OVS_PACKET_ATTR_UNUSED1", "none"),1854            ("OVS_PACKET_ATTR_UNUSED2", "none"),1855            ("OVS_PACKET_ATTR_PROBE", "none"),1856            ("OVS_PACKET_ATTR_MRU", "uint16"),1857            ("OVS_PACKET_ATTR_LEN", "uint32"),1858            ("OVS_PACKET_ATTR_HASH", "uint64"),1859        )1860 1861    def __init__(self):1862        GenericNetlinkSocket.__init__(self)1863        self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg)1864 1865    def upcall_handler(self, up=None):1866        print("listening on upcall packet handler:", self.epid)1867        while True:1868            try:1869                msgs = self.get()1870                for msg in msgs:1871                    if not up:1872                        continue1873                    if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS:1874                        up.miss(msg)1875                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION:1876                        up.action(msg)1877                    elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE:1878                        up.execute(msg)1879                    else:1880                        print("Unkonwn cmd: %d" % msg["cmd"])1881            except NetlinkError as ne:1882                raise ne1883 1884 1885class OvsDatapath(GenericNetlinkSocket):1886    OVS_DP_F_VPORT_PIDS = 1 << 11887    OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 31888 1889    class dp_cmd_msg(ovs_dp_msg):1890        """1891        Message class that will be used to communicate with the kernel module.1892        """1893 1894        nla_map = (1895            ("OVS_DP_ATTR_UNSPEC", "none"),1896            ("OVS_DP_ATTR_NAME", "asciiz"),1897            ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"),1898            ("OVS_DP_ATTR_STATS", "dpstats"),1899            ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"),1900            ("OVS_DP_ATTR_USER_FEATURES", "uint32"),1901            ("OVS_DP_ATTR_PAD", "none"),1902            ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"),1903            ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"),1904        )1905 1906        class dpstats(nla):1907            fields = (1908                ("hit", "=Q"),1909                ("missed", "=Q"),1910                ("lost", "=Q"),1911                ("flows", "=Q"),1912            )1913 1914        class megaflowstats(nla):1915            fields = (1916                ("mask_hit", "=Q"),1917                ("masks", "=I"),1918                ("padding", "=I"),1919                ("cache_hits", "=Q"),1920                ("pad1", "=Q"),1921            )1922 1923    def __init__(self):1924        GenericNetlinkSocket.__init__(self)1925        self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg)1926 1927    def info(self, dpname, ifindex=0):1928        msg = OvsDatapath.dp_cmd_msg()1929        msg["cmd"] = OVS_DP_CMD_GET1930        msg["version"] = OVS_DATAPATH_VERSION1931        msg["reserved"] = 01932        msg["dpifindex"] = ifindex1933        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])1934 1935        try:1936            reply = self.nlm_request(1937                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST1938            )1939            reply = reply[0]1940        except NetlinkError as ne:1941            if ne.code == errno.ENODEV:1942                reply = None1943            else:1944                raise ne1945 1946        return reply1947 1948    def create(1949        self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket()1950    ):1951        msg = OvsDatapath.dp_cmd_msg()1952        msg["cmd"] = OVS_DP_CMD_NEW1953        if versionStr is None:1954            msg["version"] = OVS_DATAPATH_VERSION1955        else:1956            msg["version"] = int(versionStr.split(":")[0], 0)1957        msg["reserved"] = 01958        msg["dpifindex"] = 01959        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])1960 1961        dpfeatures = 01962        if versionStr is not None and versionStr.find(":") != -1:1963            dpfeatures = int(versionStr.split(":")[1], 0)1964        else:1965            if versionStr is None or versionStr.find(":") == -1:1966                dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU1967                dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS1968 1969            nproc = multiprocessing.cpu_count()1970            procarray = []1971            for i in range(1, nproc):1972                procarray += [int(p.epid)]1973            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray])1974        msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures])1975        if not shouldUpcall:1976            msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]])1977 1978        try:1979            reply = self.nlm_request(1980                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK1981            )1982            reply = reply[0]1983        except NetlinkError as ne:1984            if ne.code == errno.EEXIST:1985                reply = None1986            else:1987                raise ne1988 1989        return reply1990 1991    def destroy(self, dpname):1992        msg = OvsDatapath.dp_cmd_msg()1993        msg["cmd"] = OVS_DP_CMD_DEL1994        msg["version"] = OVS_DATAPATH_VERSION1995        msg["reserved"] = 01996        msg["dpifindex"] = 01997        msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname])1998 1999        try:2000            reply = self.nlm_request(2001                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2002            )2003            reply = reply[0]2004        except NetlinkError as ne:2005            if ne.code == errno.ENODEV:2006                reply = None2007            else:2008                raise ne2009 2010        return reply2011 2012 2013class OvsVport(GenericNetlinkSocket):2014    OVS_VPORT_TYPE_NETDEV = 12015    OVS_VPORT_TYPE_INTERNAL = 22016    OVS_VPORT_TYPE_GRE = 32017    OVS_VPORT_TYPE_VXLAN = 42018    OVS_VPORT_TYPE_GENEVE = 52019 2020    class ovs_vport_msg(ovs_dp_msg):2021        nla_map = (2022            ("OVS_VPORT_ATTR_UNSPEC", "none"),2023            ("OVS_VPORT_ATTR_PORT_NO", "uint32"),2024            ("OVS_VPORT_ATTR_TYPE", "uint32"),2025            ("OVS_VPORT_ATTR_NAME", "asciiz"),2026            ("OVS_VPORT_ATTR_OPTIONS", "vportopts"),2027            ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"),2028            ("OVS_VPORT_ATTR_STATS", "vportstats"),2029            ("OVS_VPORT_ATTR_PAD", "none"),2030            ("OVS_VPORT_ATTR_IFINDEX", "uint32"),2031            ("OVS_VPORT_ATTR_NETNSID", "uint32"),2032        )2033 2034        class vportopts(nla):2035            nla_map = (2036                ("OVS_TUNNEL_ATTR_UNSPEC", "none"),2037                ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"),2038                ("OVS_TUNNEL_ATTR_EXTENSION", "none"),2039            )2040 2041        class vportstats(nla):2042            fields = (2043                ("rx_packets", "=Q"),2044                ("tx_packets", "=Q"),2045                ("rx_bytes", "=Q"),2046                ("tx_bytes", "=Q"),2047                ("rx_errors", "=Q"),2048                ("tx_errors", "=Q"),2049                ("rx_dropped", "=Q"),2050                ("tx_dropped", "=Q"),2051            )2052 2053    def type_to_str(vport_type):2054        if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV:2055            return "netdev"2056        elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL:2057            return "internal"2058        elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE:2059            return "gre"2060        elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN:2061            return "vxlan"2062        elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE:2063            return "geneve"2064        raise ValueError("Unknown vport type:%d" % vport_type)2065 2066    def str_to_type(vport_type):2067        if vport_type == "netdev":2068            return OvsVport.OVS_VPORT_TYPE_NETDEV2069        elif vport_type == "internal":2070            return OvsVport.OVS_VPORT_TYPE_INTERNAL2071        elif vport_type == "gre":2072            return OvsVport.OVS_VPORT_TYPE_INTERNAL2073        elif vport_type == "vxlan":2074            return OvsVport.OVS_VPORT_TYPE_VXLAN2075        elif vport_type == "geneve":2076            return OvsVport.OVS_VPORT_TYPE_GENEVE2077        raise ValueError("Unknown vport type: '%s'" % vport_type)2078 2079    def __init__(self, packet=OvsPacket()):2080        GenericNetlinkSocket.__init__(self)2081        self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg)2082        self.upcall_packet = packet2083 2084    def info(self, vport_name, dpifindex=0, portno=None):2085        msg = OvsVport.ovs_vport_msg()2086 2087        msg["cmd"] = OVS_VPORT_CMD_GET2088        msg["version"] = OVS_DATAPATH_VERSION2089        msg["reserved"] = 02090        msg["dpifindex"] = dpifindex2091 2092        if portno is None:2093            msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name])2094        else:2095            msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno])2096 2097        try:2098            reply = self.nlm_request(2099                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST2100            )2101            reply = reply[0]2102        except NetlinkError as ne:2103            if ne.code == errno.ENODEV:2104                reply = None2105            else:2106                raise ne2107        return reply2108 2109    def attach(self, dpindex, vport_ifname, ptype, dport, lwt):2110        msg = OvsVport.ovs_vport_msg()2111 2112        msg["cmd"] = OVS_VPORT_CMD_NEW2113        msg["version"] = OVS_DATAPATH_VERSION2114        msg["reserved"] = 02115        msg["dpifindex"] = dpindex2116        port_type = OvsVport.str_to_type(ptype)2117 2118        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])2119        msg["attrs"].append(2120            ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]]2121        )2122 2123        TUNNEL_DEFAULTS = [("geneve", 6081),2124                           ("vxlan", 4789)]2125 2126        for tnl in TUNNEL_DEFAULTS:2127            if ptype == tnl[0]:2128                if not dport:2129                    dport = tnl[1]2130 2131                if not lwt:2132                    vportopt = OvsVport.ovs_vport_msg.vportopts()2133                    vportopt["attrs"].append(2134                        ["OVS_TUNNEL_ATTR_DST_PORT", socket.htons(dport)]2135                    )2136                    msg["attrs"].append(2137                        ["OVS_VPORT_ATTR_OPTIONS", vportopt]2138                    )2139                else:2140                    port_type = OvsVport.OVS_VPORT_TYPE_NETDEV2141                    ipr = pyroute2.iproute.IPRoute()2142 2143                    if tnl[0] == "geneve":2144                        ipr.link("add", ifname=vport_ifname, kind=tnl[0],2145                                 geneve_port=dport,2146                                 geneve_collect_metadata=True,2147                                 geneve_udp_zero_csum6_rx=1)2148                    elif tnl[0] == "vxlan":2149                        ipr.link("add", ifname=vport_ifname, kind=tnl[0],2150                                 vxlan_learning=0, vxlan_collect_metadata=1,2151                                 vxlan_udp_zero_csum6_rx=1, vxlan_port=dport)2152                break2153        msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type])2154 2155        try:2156            reply = self.nlm_request(2157                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2158            )2159            reply = reply[0]2160        except NetlinkError as ne:2161            if ne.code == errno.EEXIST:2162                reply = None2163            else:2164                raise ne2165        return reply2166 2167    def reset_upcall(self, dpindex, vport_ifname, p=None):2168        msg = OvsVport.ovs_vport_msg()2169 2170        msg["cmd"] = OVS_VPORT_CMD_SET2171        msg["version"] = OVS_DATAPATH_VERSION2172        msg["reserved"] = 02173        msg["dpifindex"] = dpindex2174        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])2175 2176        if p == None:2177            p = self.upcall_packet2178        else:2179            self.upcall_packet = p2180 2181        msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]])2182 2183        try:2184            reply = self.nlm_request(2185                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2186            )2187            reply = reply[0]2188        except NetlinkError as ne:2189            raise ne2190        return reply2191 2192    def detach(self, dpindex, vport_ifname):2193        msg = OvsVport.ovs_vport_msg()2194 2195        msg["cmd"] = OVS_VPORT_CMD_DEL2196        msg["version"] = OVS_DATAPATH_VERSION2197        msg["reserved"] = 02198        msg["dpifindex"] = dpindex2199        msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])2200 2201        try:2202            reply = self.nlm_request(2203                msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK2204            )2205            reply = reply[0]2206        except NetlinkError as ne:2207            if ne.code == errno.ENODEV:2208                reply = None2209            else:2210                raise ne2211        return reply2212 2213    def upcall_handler(self, handler=None):2214        self.upcall_packet.upcall_handler(handler)2215 2216 2217class OvsFlow(GenericNetlinkSocket):2218    class ovs_flow_msg(ovs_dp_msg):2219        nla_map = (2220            ("OVS_FLOW_ATTR_UNSPEC", "none"),2221            ("OVS_FLOW_ATTR_KEY", "ovskey"),2222            ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"),2223            ("OVS_FLOW_ATTR_STATS", "flowstats"),2224            ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"),2225            ("OVS_FLOW_ATTR_USED", "uint64"),2226            ("OVS_FLOW_ATTR_CLEAR", "none"),2227            ("OVS_FLOW_ATTR_MASK", "ovskey"),2228            ("OVS_FLOW_ATTR_PROBE", "none"),2229            ("OVS_FLOW_ATTR_UFID", "array(uint32)"),2230            ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"),2231        )2232 2233        class flowstats(nla):2234            fields = (2235                ("packets", "=Q"),2236                ("bytes", "=Q"),2237            )2238 2239        def dpstr(self, more=False):2240            ufid = self.get_attr("OVS_FLOW_ATTR_UFID")2241            ufid_str = ""2242            if ufid is not None:2243                ufid_str = (2244                    "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format(2245                        ufid[0],2246                        ufid[1] >> 16,2247                        ufid[1] & 0xFFFF,2248                        ufid[2] >> 16,2249                        ufid[2] & 0,2250                        ufid[3],2251                    )2252                )2253 2254            key_field = self.get_attr("OVS_FLOW_ATTR_KEY")2255            keymsg = None2256            if key_field is not None:2257                keymsg = key_field2258 2259            mask_field = self.get_attr("OVS_FLOW_ATTR_MASK")2260            maskmsg = None2261            if mask_field is not None:2262                maskmsg = mask_field2263 2264            acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS")2265            actsmsg = None2266            if acts_field is not None:2267                actsmsg = acts_field2268 2269            print_str = ""2270 2271            if more:2272                print_str += ufid_str + ","2273 2274            if keymsg is not None:2275                print_str += keymsg.dpstr(maskmsg, more)2276 2277            stats = self.get_attr("OVS_FLOW_ATTR_STATS")2278            if stats is None:2279                print_str += " packets:0, bytes:0,"2280            else:2281                print_str += " packets:%d, bytes:%d," % (2282                    stats["packets"],2283                    stats["bytes"],2284                )2285 2286            used = self.get_attr("OVS_FLOW_ATTR_USED")2287            print_str += " used:"2288            if used is None:2289                print_str += "never,"2290            else:2291                used_time = int(used)2292                cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC)2293                used_time = (cur_time_sec * 1000) - used_time2294                print_str += "{}s,".format(used_time / 1000)2295 2296            print_str += " actions:"2297            if (2298                actsmsg is None2299                or "attrs" not in actsmsg2300                or len(actsmsg["attrs"]) == 02301            ):2302                print_str += "drop"2303            else:2304                print_str += actsmsg.dpstr(more)2305 2306            return print_str2307 2308        def parse(self, flowstr, actstr, dpidx=0):2309            OVS_UFID_F_OMIT_KEY = 1 << 02310            OVS_UFID_F_OMIT_MASK = 1 << 12311            OVS_UFID_F_OMIT_ACTIONS = 1 << 22312 2313            self["cmd"] = 02314            self["version"] = 02315            self["reserved"] = 02316            self["dpifindex"] = 02317 2318            if flowstr.startswith("ufid:"):2319                count = 52320                while flowstr[count] != ",":2321                    count += 12322                ufidstr = flowstr[5:count]2323                flowstr = flowstr[count + 1 :]2324            else:2325                ufidstr = str(uuid.uuid4())2326            uuidRawObj = uuid.UUID(ufidstr).fields2327 2328            self["attrs"].append(2329                [2330                    "OVS_FLOW_ATTR_UFID",2331                    [2332                        uuidRawObj[0],2333                        uuidRawObj[1] << 16 | uuidRawObj[2],2334                        uuidRawObj[3] << 242335                        | uuidRawObj[4] << 162336                        | uuidRawObj[5] & (0xFF << 32) >> 32,2337                        uuidRawObj[5] & (0xFFFFFFFF),2338                    ],2339                ]2340            )2341            self["attrs"].append(2342                [2343                    "OVS_FLOW_ATTR_UFID_FLAGS",2344                    int(2345                        OVS_UFID_F_OMIT_KEY2346                        | OVS_UFID_F_OMIT_MASK2347                        | OVS_UFID_F_OMIT_ACTIONS2348                    ),2349                ]2350            )2351 2352            k = ovskey()2353            m = ovskey()2354            k.parse(flowstr, m)2355            self["attrs"].append(["OVS_FLOW_ATTR_KEY", k])2356            self["attrs"].append(["OVS_FLOW_ATTR_MASK", m])2357 2358            a = ovsactions()2359            a.parse(actstr)2360            self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a])2361 2362    def __init__(self):2363        GenericNetlinkSocket.__init__(self)2364 2365        self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg)2366 2367    def add_flow(self, dpifindex, flowmsg):2368        """2369        Send a new flow message to the kernel.2370 2371        dpifindex should be a valid datapath obtained by calling2372        into the OvsDatapath lookup2373 2374        flowmsg is a flow object obtained by calling a dpparse2375        """2376 2377        flowmsg["cmd"] = OVS_FLOW_CMD_NEW2378        flowmsg["version"] = OVS_DATAPATH_VERSION2379        flowmsg["reserved"] = 02380        flowmsg["dpifindex"] = dpifindex2381 2382        try:2383            reply = self.nlm_request(2384                flowmsg,2385                msg_type=self.prid,2386                msg_flags=NLM_F_REQUEST | NLM_F_ACK,2387            )2388            reply = reply[0]2389        except NetlinkError as ne:2390            print(flowmsg)2391            raise ne2392        return reply2393 2394    def del_flows(self, dpifindex):2395        """2396        Send a del message to the kernel that will drop all flows.2397 2398        dpifindex should be a valid datapath obtained by calling2399        into the OvsDatapath lookup2400        """2401 2402        flowmsg = OvsFlow.ovs_flow_msg()2403        flowmsg["cmd"] = OVS_FLOW_CMD_DEL2404        flowmsg["version"] = OVS_DATAPATH_VERSION2405        flowmsg["reserved"] = 02406        flowmsg["dpifindex"] = dpifindex2407 2408        try:2409            reply = self.nlm_request(2410                flowmsg,2411                msg_type=self.prid,2412                msg_flags=NLM_F_REQUEST | NLM_F_ACK,2413            )2414            reply = reply[0]2415        except NetlinkError as ne:2416            print(flowmsg)2417            raise ne2418        return reply2419 2420    def dump(self, dpifindex, flowspec=None):2421        """2422        Returns a list of messages containing flows.2423 2424        dpifindex should be a valid datapath obtained by calling2425        into the OvsDatapath lookup2426 2427        flowpsec is a string which represents a flow in the dpctl2428        format.2429        """2430        msg = OvsFlow.ovs_flow_msg()2431 2432        msg["cmd"] = OVS_FLOW_CMD_GET2433        msg["version"] = OVS_DATAPATH_VERSION2434        msg["reserved"] = 02435        msg["dpifindex"] = dpifindex2436 2437        msg_flags = NLM_F_REQUEST | NLM_F_ACK2438        if flowspec is None:2439            msg_flags |= NLM_F_DUMP2440        rep = None2441 2442        try:2443            rep = self.nlm_request(2444                msg,2445                msg_type=self.prid,2446                msg_flags=msg_flags,2447            )2448        except NetlinkError as ne:2449            raise ne2450        return rep2451 2452    def miss(self, packetmsg):2453        seq = packetmsg["header"]["sequence_number"]2454        keystr = "(none)"2455        key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY")2456        if key_field is not None:2457            keystr = key_field.dpstr(None, True)2458 2459        pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET")2460        pktpres = "yes" if pktdata is not None else "no"2461 2462        print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True)2463 2464    def execute(self, packetmsg):2465        print("userspace execute command", flush=True)2466 2467    def action(self, packetmsg):2468        print("userspace action command", flush=True)2469 2470 2471class psample_sample(genlmsg):2472    nla_map = (2473        ("PSAMPLE_ATTR_IIFINDEX", "none"),2474        ("PSAMPLE_ATTR_OIFINDEX", "none"),2475        ("PSAMPLE_ATTR_ORIGSIZE", "none"),2476        ("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"),2477        ("PSAMPLE_ATTR_GROUP_SEQ", "none"),2478        ("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"),2479        ("PSAMPLE_ATTR_DATA", "array(uint8)"),2480        ("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"),2481        ("PSAMPLE_ATTR_TUNNEL", "none"),2482        ("PSAMPLE_ATTR_PAD", "none"),2483        ("PSAMPLE_ATTR_OUT_TC", "none"),2484        ("PSAMPLE_ATTR_OUT_TC_OCC", "none"),2485        ("PSAMPLE_ATTR_LATENCY", "none"),2486        ("PSAMPLE_ATTR_TIMESTAMP", "none"),2487        ("PSAMPLE_ATTR_PROTO", "none"),2488        ("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"),2489    )2490 2491    def dpstr(self):2492        fields = []2493        data = ""2494        for (attr, value) in self["attrs"]:2495            if attr == "PSAMPLE_ATTR_SAMPLE_GROUP":2496                fields.append("group:%d" % value)2497            if attr == "PSAMPLE_ATTR_SAMPLE_RATE":2498                fields.append("rate:%d" % value)2499            if attr == "PSAMPLE_ATTR_USER_COOKIE":2500                value = "".join(format(x, "02x") for x in value)2501                fields.append("cookie:%s" % value)2502            if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0:2503                data = "data:%s" % "".join(format(x, "02x") for x in value)2504 2505        return ("%s %s" % (",".join(fields), data)).strip()2506 2507 2508class psample_msg(Marshal):2509    PSAMPLE_CMD_SAMPLE = 02510    PSAMPLE_CMD_GET_GROUP = 12511    PSAMPLE_CMD_NEW_GROUP = 22512    PSAMPLE_CMD_DEL_GROUP = 32513    PSAMPLE_CMD_SET_FILTER = 42514    msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample}2515 2516 2517class PsampleEvent(EventSocket):2518    genl_family = "psample"2519    mcast_groups = ["packets"]2520    marshal_class = psample_msg2521 2522    def read_samples(self):2523        print("listening for psample events", flush=True)2524        while True:2525            try:2526                for msg in self.get():2527                    print(msg.dpstr(), flush=True)2528            except NetlinkError as ne:2529                raise ne2530 2531 2532def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):2533    dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")2534    base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS")2535    megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS")2536    user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES")2537    masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE")2538 2539    print("%s:" % dp_name)2540    print(2541        "  lookups: hit:%d missed:%d lost:%d"2542        % (base_stats["hit"], base_stats["missed"], base_stats["lost"])2543    )2544    print("  flows:%d" % base_stats["flows"])2545    pkts = base_stats["hit"] + base_stats["missed"]2546    avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.02547    print(2548        "  masks: hit:%d total:%d hit/pkt:%f"2549        % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg)2550    )2551    print("  caches:")2552    print("    masks-cache: size:%d" % masks_cache_size)2553 2554    if user_features is not None:2555        print("  features: 0x%X" % user_features)2556 2557    # port print out2558    for iface in ndb.interfaces:2559        rep = vpl.info(iface.ifname, ifindex)2560        if rep is not None:2561            opts = ""2562            vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS")2563            if vpo:2564                dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT")2565                if dpo:2566                    opts += " tnl-dport:%s" % socket.ntohs(dpo)2567            print(2568                "  port %d: %s (%s%s)"2569                % (2570                    rep.get_attr("OVS_VPORT_ATTR_PORT_NO"),2571                    rep.get_attr("OVS_VPORT_ATTR_NAME"),2572                    OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")),2573                    opts,2574                )2575            )2576 2577 2578def main(argv):2579    nlmsg_atoms.ovskey = ovskey2580    nlmsg_atoms.ovsactions = ovsactions2581 2582    # version check for pyroute22583    prverscheck = pyroute2.__version__.split(".")2584    if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6:2585        print("Need to upgrade the python pyroute2 package to >= 0.6.")2586        sys.exit(0)2587 2588    parser = argparse.ArgumentParser()2589    parser.add_argument(2590        "-v",2591        "--verbose",2592        action="count",2593        help="Increment 'verbose' output counter.",2594        default=0,2595    )2596    subparsers = parser.add_subparsers(dest="subcommand")2597 2598    showdpcmd = subparsers.add_parser("show")2599    showdpcmd.add_argument(2600        "showdp", metavar="N", type=str, nargs="?", help="Datapath Name"2601    )2602 2603    adddpcmd = subparsers.add_parser("add-dp")2604    adddpcmd.add_argument("adddp", help="Datapath Name")2605    adddpcmd.add_argument(2606        "-u",2607        "--upcall",2608        action="store_true",2609        help="Leave open a reader for upcalls",2610    )2611    adddpcmd.add_argument(2612        "-V",2613        "--versioning",2614        required=False,2615        help="Specify a custom version / feature string",2616    )2617 2618    deldpcmd = subparsers.add_parser("del-dp")2619    deldpcmd.add_argument("deldp", help="Datapath Name")2620 2621    addifcmd = subparsers.add_parser("add-if")2622    addifcmd.add_argument("dpname", help="Datapath Name")2623    addifcmd.add_argument("addif", help="Interface name for adding")2624    addifcmd.add_argument(2625        "-u",2626        "--upcall",2627        action="store_true",2628        help="Leave open a reader for upcalls",2629    )2630    addifcmd.add_argument(2631        "-t",2632        "--ptype",2633        type=str,2634        default="netdev",2635        choices=["netdev", "internal", "geneve", "vxlan"],2636        help="Interface type (default netdev)",2637    )2638    addifcmd.add_argument(2639        "-p",2640        "--dport",2641        type=int,2642        default=0,2643        help="Destination port (0 for default)"2644    )2645    addifcmd.add_argument(2646        "-l",2647        "--lwt",2648        type=bool,2649        default=True,2650        help="Use LWT infrastructure instead of vport (default true)."2651    )2652    delifcmd = subparsers.add_parser("del-if")2653    delifcmd.add_argument("dpname", help="Datapath Name")2654    delifcmd.add_argument("delif", help="Interface name for adding")2655    delifcmd.add_argument("-d",2656                          "--dellink",2657                          type=bool, default=False,2658                          help="Delete the link as well.")2659 2660    dumpflcmd = subparsers.add_parser("dump-flows")2661    dumpflcmd.add_argument("dumpdp", help="Datapath Name")2662 2663    addflcmd = subparsers.add_parser("add-flow")2664    addflcmd.add_argument("flbr", help="Datapath name")2665    addflcmd.add_argument("flow", help="Flow specification")2666    addflcmd.add_argument("acts", help="Flow actions")2667 2668    delfscmd = subparsers.add_parser("del-flows")2669    delfscmd.add_argument("flsbr", help="Datapath name")2670 2671    subparsers.add_parser("psample-events")2672 2673    args = parser.parse_args()2674 2675    if args.verbose > 0:2676        if args.verbose > 1:2677            logging.basicConfig(level=logging.DEBUG)2678 2679    ovspk = OvsPacket()2680    ovsdp = OvsDatapath()2681    ovsvp = OvsVport(ovspk)2682    ovsflow = OvsFlow()2683    ndb = NDB()2684 2685    sys.setrecursionlimit(100000)2686 2687    if args.subcommand == "psample-events":2688        PsampleEvent().read_samples()2689 2690    if hasattr(args, "showdp"):2691        found = False2692        for iface in ndb.interfaces:2693            rep = None2694            if args.showdp is None:2695                rep = ovsdp.info(iface.ifname, 0)2696            elif args.showdp == iface.ifname:2697                rep = ovsdp.info(iface.ifname, 0)2698 2699            if rep is not None:2700                found = True2701                print_ovsdp_full(rep, iface.index, ndb, ovsvp)2702 2703        if not found:2704            msg = "No DP found"2705            if args.showdp is not None:2706                msg += ":'%s'" % args.showdp2707            print(msg)2708    elif hasattr(args, "adddp"):2709        rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk)2710        if rep is None:2711            print("DP '%s' already exists" % args.adddp)2712        else:2713            print("DP '%s' added" % args.adddp)2714        if args.upcall:2715            ovspk.upcall_handler(ovsflow)2716    elif hasattr(args, "deldp"):2717        ovsdp.destroy(args.deldp)2718    elif hasattr(args, "addif"):2719        rep = ovsdp.info(args.dpname, 0)2720        if rep is None:2721            print("DP '%s' not found." % args.dpname)2722            return 12723        dpindex = rep["dpifindex"]2724        rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype,2725                           args.dport, args.lwt)2726        msg = "vport '%s'" % args.addif2727        if rep and rep["header"]["error"] is None:2728            msg += " added."2729        else:2730            msg += " failed to add."2731        if args.upcall:2732            if rep is None:2733                rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk)2734            ovsvp.upcall_handler(ovsflow)2735    elif hasattr(args, "delif"):2736        rep = ovsdp.info(args.dpname, 0)2737        if rep is None:2738            print("DP '%s' not found." % args.dpname)2739            return 12740        rep = ovsvp.detach(rep["dpifindex"], args.delif)2741        msg = "vport '%s'" % args.delif2742        if rep and rep["header"]["error"] is None:2743            msg += " removed."2744        else:2745            msg += " failed to remove."2746        if args.dellink:2747            ipr = pyroute2.iproute.IPRoute()2748            ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0])2749    elif hasattr(args, "dumpdp"):2750        rep = ovsdp.info(args.dumpdp, 0)2751        if rep is None:2752            print("DP '%s' not found." % args.dumpdp)2753            return 12754        rep = ovsflow.dump(rep["dpifindex"])2755        for flow in rep:2756            print(flow.dpstr(True if args.verbose > 0 else False))2757    elif hasattr(args, "flbr"):2758        rep = ovsdp.info(args.flbr, 0)2759        if rep is None:2760            print("DP '%s' not found." % args.flbr)2761            return 12762        flow = OvsFlow.ovs_flow_msg()2763        flow.parse(args.flow, args.acts, rep["dpifindex"])2764        ovsflow.add_flow(rep["dpifindex"], flow)2765    elif hasattr(args, "flsbr"):2766        rep = ovsdp.info(args.flsbr, 0)2767        if rep is None:2768            print("DP '%s' not found." % args.flsbr)2769        ovsflow.del_flows(rep["dpifindex"])2770 2771    return 02772 2773 2774if __name__ == "__main__":2775    sys.exit(main(sys.argv))2776