| #!/usr/bin/env python3 |
| # SPDX-License-Identifier: GPL-2.0 |
| |
| # Controls the openvswitch module. Part of the kselftest suite, but |
| # can be used for some diagnostic purpose as well. |
| |
| import argparse |
| import errno |
| import ipaddress |
| import logging |
| import math |
| import multiprocessing |
| import re |
| import socket |
| import struct |
| import sys |
| import time |
| import types |
| import uuid |
| |
| try: |
| from pyroute2 import NDB |
| |
| from pyroute2.netlink import NLA_F_NESTED |
| from pyroute2.netlink import NLM_F_ACK |
| from pyroute2.netlink import NLM_F_DUMP |
| from pyroute2.netlink import NLM_F_REQUEST |
| from pyroute2.netlink import genlmsg |
| from pyroute2.netlink import nla |
| from pyroute2.netlink import nlmsg_atoms |
| from pyroute2.netlink.event import EventSocket |
| from pyroute2.netlink.exceptions import NetlinkError |
| from pyroute2.netlink.generic import GenericNetlinkSocket |
| from pyroute2.netlink.nlsocket import Marshal |
| import pyroute2 |
| import pyroute2.iproute |
| |
| except ModuleNotFoundError: |
| print("Need to install the python pyroute2 package >= 0.6.") |
| sys.exit(1) |
| |
| |
| OVS_DATAPATH_FAMILY = "ovs_datapath" |
| OVS_VPORT_FAMILY = "ovs_vport" |
| OVS_FLOW_FAMILY = "ovs_flow" |
| OVS_PACKET_FAMILY = "ovs_packet" |
| OVS_METER_FAMILY = "ovs_meter" |
| OVS_CT_LIMIT_FAMILY = "ovs_ct_limit" |
| |
| OVS_DATAPATH_VERSION = 2 |
| OVS_DP_CMD_NEW = 1 |
| OVS_DP_CMD_DEL = 2 |
| OVS_DP_CMD_GET = 3 |
| OVS_DP_CMD_SET = 4 |
| |
| OVS_VPORT_CMD_NEW = 1 |
| OVS_VPORT_CMD_DEL = 2 |
| OVS_VPORT_CMD_GET = 3 |
| OVS_VPORT_CMD_SET = 4 |
| |
| OVS_FLOW_CMD_NEW = 1 |
| OVS_FLOW_CMD_DEL = 2 |
| OVS_FLOW_CMD_GET = 3 |
| OVS_FLOW_CMD_SET = 4 |
| |
| UINT32_MAX = 0xFFFFFFFF |
| |
| def macstr(mac): |
| outstr = ":".join(["%02X" % i for i in mac]) |
| return outstr |
| |
| |
| def strcspn(str1, str2): |
| tot = 0 |
| for char in str1: |
| if str2.find(char) != -1: |
| return tot |
| tot += 1 |
| return tot |
| |
| |
| def strspn(str1, str2): |
| tot = 0 |
| for char in str1: |
| if str2.find(char) == -1: |
| return tot |
| tot += 1 |
| return tot |
| |
| |
| def intparse(statestr, defmask="0xffffffff"): |
| totalparse = strspn(statestr, "0123456789abcdefABCDEFx/") |
| # scan until "/" |
| count = strspn(statestr, "x0123456789abcdefABCDEF") |
| |
| firstnum = statestr[:count] |
| if firstnum[-1] == "/": |
| firstnum = firstnum[:-1] |
| k = int(firstnum, 0) |
| |
| m = None |
| if defmask is not None: |
| secondnum = defmask |
| if statestr[count] == "/": |
| secondnum = statestr[count + 1 :] # this is wrong... |
| m = int(secondnum, 0) |
| |
| return statestr[totalparse + 1 :], k, m |
| |
| |
| def parse_flags(flag_str, flag_vals): |
| bitResult = 0 |
| maskResult = 0 |
| |
| if len(flag_str) == 0: |
| return flag_str, bitResult, maskResult |
| |
| if flag_str[0].isdigit(): |
| idx = 0 |
| while flag_str[idx].isdigit() or flag_str[idx] == "x": |
| idx += 1 |
| digits = flag_str[:idx] |
| flag_str = flag_str[idx:] |
| |
| bitResult = int(digits, 0) |
| maskResult = int(digits, 0) |
| |
| while len(flag_str) > 0 and (flag_str[0] == "+" or flag_str[0] == "-"): |
| if flag_str[0] == "+": |
| setFlag = True |
| elif flag_str[0] == "-": |
| setFlag = False |
| |
| flag_str = flag_str[1:] |
| |
| flag_len = 0 |
| while ( |
| flag_str[flag_len] != "+" |
| and flag_str[flag_len] != "-" |
| and flag_str[flag_len] != "," |
| and flag_str[flag_len] != ")" |
| ): |
| flag_len += 1 |
| |
| flag = flag_str[0:flag_len] |
| |
| if flag in flag_vals: |
| if maskResult & flag_vals[flag]: |
| raise KeyError( |
| "Flag %s set once, cannot be set in multiples" % flag |
| ) |
| |
| if setFlag: |
| bitResult |= flag_vals[flag] |
| |
| maskResult |= flag_vals[flag] |
| else: |
| raise KeyError("Missing flag value: %s" % flag) |
| |
| flag_str = flag_str[flag_len:] |
| |
| return flag_str, bitResult, maskResult |
| |
| |
| def parse_ct_state(statestr): |
| ct_flags = { |
| "new": 1 << 0, |
| "est": 1 << 1, |
| "rel": 1 << 2, |
| "rpl": 1 << 3, |
| "inv": 1 << 4, |
| "trk": 1 << 5, |
| "snat": 1 << 6, |
| "dnat": 1 << 7, |
| } |
| |
| return parse_flags(statestr, ct_flags) |
| |
| |
| def convert_mac(data): |
| def to_bytes(mac): |
| mac_split = mac.split(":") |
| ret = bytearray([int(i, 16) for i in mac_split]) |
| return bytes(ret) |
| |
| mac_str, _, mask_str = data.partition('/') |
| |
| if not mac_str: |
| mac_str = mask_str = "00:00:00:00:00:00" |
| elif not mask_str: |
| mask_str = "FF:FF:FF:FF:FF:FF" |
| |
| return to_bytes(mac_str), to_bytes(mask_str) |
| |
| def convert_ipv4(data): |
| ip, _, mask = data.partition('/') |
| |
| if not ip: |
| ip = mask = 0 |
| elif not mask: |
| mask = 0xFFFFFFFF |
| elif mask.isdigit(): |
| mask = (0xFFFFFFFF << (32 - int(mask))) & 0xFFFFFFFF |
| |
| return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask)) |
| |
| def convert_ipv6(data): |
| ip, _, mask = data.partition('/') |
| |
| if not ip: |
| ip = mask = 0 |
| elif not mask: |
| mask = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' |
| elif mask.isdigit(): |
| mask = ipaddress.IPv6Network("::/" + mask).hostmask |
| |
| return ipaddress.IPv6Address(ip).packed, ipaddress.IPv6Address(mask).packed |
| |
| def convert_int(size): |
| def convert_int_sized(data): |
| value, _, mask = data.partition('/') |
| |
| if not value: |
| return 0, 0 |
| elif not mask: |
| return int(value, 0), pow(2, size) - 1 |
| else: |
| return int(value, 0), int(mask, 0) |
| |
| return convert_int_sized |
| |
| def parse_starts_block(block_str, scanstr, returnskipped, scanregex=False): |
| if scanregex: |
| m = re.search(scanstr, block_str) |
| if m is None: |
| if returnskipped: |
| return block_str |
| return False |
| if returnskipped: |
| block_str = block_str[len(m.group(0)) :] |
| return block_str |
| return True |
| |
| if block_str.startswith(scanstr): |
| if returnskipped: |
| block_str = block_str[len(scanstr) :] |
| else: |
| return True |
| |
| if returnskipped: |
| return block_str |
| |
| return False |
| |
| |
| def parse_extract_field( |
| block_str, fieldstr, scanfmt, convert, masked=False, defval=None |
| ): |
| if fieldstr and not block_str.startswith(fieldstr): |
| return block_str, defval |
| |
| if fieldstr: |
| str_skiplen = len(fieldstr) |
| str_skipped = block_str[str_skiplen:] |
| if str_skiplen == 0: |
| return str_skipped, defval |
| else: |
| str_skiplen = 0 |
| str_skipped = block_str |
| |
| m = re.search(scanfmt, str_skipped) |
| if m is None: |
| raise ValueError("Bad fmt string") |
| |
| data = m.group(0) |
| if convert: |
| data = convert(m.group(0)) |
| |
| str_skipped = str_skipped[len(m.group(0)) :] |
| if masked: |
| if str_skipped[0] == "/": |
| raise ValueError("Masking support TBD...") |
| |
| str_skipped = str_skipped[strspn(str_skipped, ", ") :] |
| return str_skipped, data |
| |
| |
| def parse_attrs(actstr, attr_desc): |
| """Parses the given action string and returns a list of netlink |
| attributes based on a list of attribute descriptions. |
| |
| Each element in the attribute description list is a tuple such as: |
| (name, attr_name, parse_func) |
| where: |
| name: is the string representing the attribute |
| attr_name: is the name of the attribute as defined in the uAPI. |
| parse_func: is a callable accepting a string and returning either |
| a single object (the parsed attribute value) or a tuple of |
| two values (the parsed attribute value and the remaining string) |
| |
| Returns a list of attributes and the remaining string. |
| """ |
| def parse_attr(actstr, key, func): |
| actstr = actstr[len(key) :] |
| |
| if not func: |
| return None, actstr |
| |
| delim = actstr[0] |
| actstr = actstr[1:] |
| |
| if delim == "=": |
| pos = strcspn(actstr, ",)") |
| ret = func(actstr[:pos]) |
| else: |
| ret = func(actstr) |
| |
| if isinstance(ret, tuple): |
| (datum, actstr) = ret |
| else: |
| datum = ret |
| actstr = actstr[strcspn(actstr, ",)"):] |
| |
| if delim == "(": |
| if not actstr or actstr[0] != ")": |
| raise ValueError("Action contains unbalanced parentheses") |
| |
| actstr = actstr[1:] |
| |
| actstr = actstr[strspn(actstr, ", ") :] |
| |
| return datum, actstr |
| |
| attrs = [] |
| attr_desc = list(attr_desc) |
| while actstr and actstr[0] != ")" and attr_desc: |
| found = False |
| for i, (key, attr, func) in enumerate(attr_desc): |
| if actstr.startswith(key): |
| datum, actstr = parse_attr(actstr, key, func) |
| attrs.append([attr, datum]) |
| found = True |
| del attr_desc[i] |
| |
| if not found: |
| raise ValueError("Unknown attribute: '%s'" % actstr) |
| |
| actstr = actstr[strspn(actstr, ", ") :] |
| |
| if actstr[0] != ")": |
| raise ValueError("Action string contains extra garbage or has " |
| "unbalanced parenthesis: '%s'" % actstr) |
| |
| return attrs, actstr[1:] |
| |
| |
| class ovs_dp_msg(genlmsg): |
| # include the OVS version |
| # We need a custom header rather than just being able to rely on |
| # genlmsg because fields ends up not expressing everything correctly |
| # if we use the canonical example of setting fields = (('customfield',),) |
| fields = genlmsg.fields + (("dpifindex", "I"),) |
| |
| |
| class ovsactions(nla): |
| nla_flags = NLA_F_NESTED |
| |
| nla_map = ( |
| ("OVS_ACTION_ATTR_UNSPEC", "none"), |
| ("OVS_ACTION_ATTR_OUTPUT", "uint32"), |
| ("OVS_ACTION_ATTR_USERSPACE", "userspace"), |
| ("OVS_ACTION_ATTR_SET", "ovskey"), |
| ("OVS_ACTION_ATTR_PUSH_VLAN", "none"), |
| ("OVS_ACTION_ATTR_POP_VLAN", "flag"), |
| ("OVS_ACTION_ATTR_SAMPLE", "sample"), |
| ("OVS_ACTION_ATTR_RECIRC", "uint32"), |
| ("OVS_ACTION_ATTR_HASH", "none"), |
| ("OVS_ACTION_ATTR_PUSH_MPLS", "none"), |
| ("OVS_ACTION_ATTR_POP_MPLS", "flag"), |
| ("OVS_ACTION_ATTR_SET_MASKED", "ovskey"), |
| ("OVS_ACTION_ATTR_CT", "ctact"), |
| ("OVS_ACTION_ATTR_TRUNC", "uint32"), |
| ("OVS_ACTION_ATTR_PUSH_ETH", "none"), |
| ("OVS_ACTION_ATTR_POP_ETH", "flag"), |
| ("OVS_ACTION_ATTR_CT_CLEAR", "flag"), |
| ("OVS_ACTION_ATTR_PUSH_NSH", "none"), |
| ("OVS_ACTION_ATTR_POP_NSH", "flag"), |
| ("OVS_ACTION_ATTR_METER", "none"), |
| ("OVS_ACTION_ATTR_CLONE", "recursive"), |
| ("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"), |
| ("OVS_ACTION_ATTR_ADD_MPLS", "none"), |
| ("OVS_ACTION_ATTR_DEC_TTL", "none"), |
| ("OVS_ACTION_ATTR_DROP", "uint32"), |
| ("OVS_ACTION_ATTR_PSAMPLE", "psample"), |
| ) |
| |
| class psample(nla): |
| nla_flags = NLA_F_NESTED |
| |
| nla_map = ( |
| ("OVS_PSAMPLE_ATTR_UNSPEC", "none"), |
| ("OVS_PSAMPLE_ATTR_GROUP", "uint32"), |
| ("OVS_PSAMPLE_ATTR_COOKIE", "array(uint8)"), |
| ) |
| |
| def dpstr(self, more=False): |
| args = "group=%d" % self.get_attr("OVS_PSAMPLE_ATTR_GROUP") |
| |
| cookie = self.get_attr("OVS_PSAMPLE_ATTR_COOKIE") |
| if cookie: |
| args += ",cookie(%s)" % \ |
| "".join(format(x, "02x") for x in cookie) |
| |
| return "psample(%s)" % args |
| |
| def parse(self, actstr): |
| desc = ( |
| ("group", "OVS_PSAMPLE_ATTR_GROUP", int), |
| ("cookie", "OVS_PSAMPLE_ATTR_COOKIE", |
| lambda x: list(bytearray.fromhex(x))) |
| ) |
| |
| attrs, actstr = parse_attrs(actstr, desc) |
| |
| for attr in attrs: |
| self["attrs"].append(attr) |
| |
| return actstr |
| |
| class sample(nla): |
| nla_flags = NLA_F_NESTED |
| |
| nla_map = ( |
| ("OVS_SAMPLE_ATTR_UNSPEC", "none"), |
| ("OVS_SAMPLE_ATTR_PROBABILITY", "uint32"), |
| ("OVS_SAMPLE_ATTR_ACTIONS", "ovsactions"), |
| ) |
| |
| def dpstr(self, more=False): |
| args = [] |
| |
| args.append("sample={:.2f}%".format( |
| 100 * self.get_attr("OVS_SAMPLE_ATTR_PROBABILITY") / |
| UINT32_MAX)) |
| |
| actions = self.get_attr("OVS_SAMPLE_ATTR_ACTIONS") |
| if actions: |
| args.append("actions(%s)" % actions.dpstr(more)) |
| |
| return "sample(%s)" % ",".join(args) |
| |
| def parse(self, actstr): |
| def parse_nested_actions(actstr): |
| subacts = ovsactions() |
| parsed_len = subacts.parse(actstr) |
| return subacts, actstr[parsed_len :] |
| |
| def percent_to_rate(percent): |
| percent = float(percent.strip('%')) |
| return int(math.floor(UINT32_MAX * (percent / 100.0) + .5)) |
| |
| desc = ( |
| ("sample", "OVS_SAMPLE_ATTR_PROBABILITY", percent_to_rate), |
| ("actions", "OVS_SAMPLE_ATTR_ACTIONS", parse_nested_actions), |
| ) |
| attrs, actstr = parse_attrs(actstr, desc) |
| |
| for attr in attrs: |
| self["attrs"].append(attr) |
| |
| return actstr |
| |
| class ctact(nla): |
| nla_flags = NLA_F_NESTED |
| |
| nla_map = ( |
| ("OVS_CT_ATTR_NONE", "none"), |
| ("OVS_CT_ATTR_COMMIT", "flag"), |
| ("OVS_CT_ATTR_ZONE", "uint16"), |
| ("OVS_CT_ATTR_MARK", "none"), |
| ("OVS_CT_ATTR_LABELS", "none"), |
| ("OVS_CT_ATTR_HELPER", "asciiz"), |
| ("OVS_CT_ATTR_NAT", "natattr"), |
| ("OVS_CT_ATTR_FORCE_COMMIT", "flag"), |
| ("OVS_CT_ATTR_EVENTMASK", "uint32"), |
| ("OVS_CT_ATTR_TIMEOUT", "asciiz"), |
| ) |
| |
| class natattr(nla): |
| nla_flags = NLA_F_NESTED |
| |
| nla_map = ( |
| ("OVS_NAT_ATTR_NONE", "none"), |
| ("OVS_NAT_ATTR_SRC", "flag"), |
| ("OVS_NAT_ATTR_DST", "flag"), |
| ("OVS_NAT_ATTR_IP_MIN", "ipaddr"), |
| ("OVS_NAT_ATTR_IP_MAX", "ipaddr"), |
| ("OVS_NAT_ATTR_PROTO_MIN", "uint16"), |
| ("OVS_NAT_ATTR_PROTO_MAX", "uint16"), |
| ("OVS_NAT_ATTR_PERSISTENT", "flag"), |
| ("OVS_NAT_ATTR_PROTO_HASH", "flag"), |
| ("OVS_NAT_ATTR_PROTO_RANDOM", "flag"), |
| ) |
| |
| def dpstr(self, more=False): |
| print_str = "nat(" |
| |
| if self.get_attr("OVS_NAT_ATTR_SRC"): |
| print_str += "src" |
| elif self.get_attr("OVS_NAT_ATTR_DST"): |
| print_str += "dst" |
| else: |
| print_str += "XXX-unknown-nat" |
| |
| if self.get_attr("OVS_NAT_ATTR_IP_MIN") or self.get_attr( |
| "OVS_NAT_ATTR_IP_MAX" |
| ): |
| if self.get_attr("OVS_NAT_ATTR_IP_MIN"): |
| print_str += "=%s," % str( |
| self.get_attr("OVS_NAT_ATTR_IP_MIN") |
| ) |
| |
| if self.get_attr("OVS_NAT_ATTR_IP_MAX"): |
| print_str += "-%s," % str( |
| self.get_attr("OVS_NAT_ATTR_IP_MAX") |
| ) |
| else: |
| print_str += "," |
| |
| if self.get_attr("OVS_NAT_ATTR_PROTO_MIN"): |
| print_str += "proto_min=%d," % self.get_attr( |
| "OVS_NAT_ATTR_PROTO_MIN" |
| ) |
| |
| if self.get_attr("OVS_NAT_ATTR_PROTO_MAX"): |
| print_str += "proto_max=%d," % self.get_attr( |
| "OVS_NAT_ATTR_PROTO_MAX" |
| ) |
| |
| if self.get_attr("OVS_NAT_ATTR_PERSISTENT"): |
| print_str += "persistent," |
| if self.get_attr("OVS_NAT_ATTR_HASH"): |
| print_str += "hash," |
| if self.get_attr("OVS_NAT_ATTR_RANDOM"): |
| print_str += "random" |
| print_str += ")" |
| return print_str |
| |
| def dpstr(self, more=False): |
| print_str = "ct(" |
| |
| if self.get_attr("OVS_CT_ATTR_COMMIT") is not None: |
| print_str += "commit," |
| if self.get_attr("OVS_CT_ATTR_ZONE") is not None: |
| print_str += "zone=%d," % self.get_attr("OVS_CT_ATTR_ZONE") |
| if self.get_attr("OVS_CT_ATTR_HELPER") is not None: |
| print_str += "helper=%s," % self.get_attr("OVS_CT_ATTR_HELPER") |
| if self.get_attr("OVS_CT_ATTR_NAT") is not None: |
| print_str += self.get_attr("OVS_CT_ATTR_NAT").dpstr(more) |
| print_str += "," |
| if self.get_attr("OVS_CT_ATTR_FORCE_COMMIT") is not None: |
| print_str += "force," |
| if self.get_attr("OVS_CT_ATTR_EVENTMASK") is not None: |
| print_str += "emask=0x%X," % self.get_attr( |
| "OVS_CT_ATTR_EVENTMASK" |
| ) |
| if self.get_attr("OVS_CT_ATTR_TIMEOUT") is not None: |
| print_str += "timeout=%s" % self.get_attr( |
| "OVS_CT_ATTR_TIMEOUT" |
| ) |
| print_str += ")" |
| return print_str |
| |
| class userspace(nla): |
| nla_flags = NLA_F_NESTED |
| |
| nla_map = ( |
| ("OVS_USERSPACE_ATTR_UNUSED", "none"), |
| ("OVS_USERSPACE_ATTR_PID", "uint32"), |
| ("OVS_USERSPACE_ATTR_USERDATA", "array(uint8)"), |
| ("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", "uint32"), |
| ) |
| |
| def dpstr(self, more=False): |
| print_str = "userspace(" |
| if self.get_attr("OVS_USERSPACE_ATTR_PID") is not None: |
| print_str += "pid=%d," % self.get_attr( |
| "OVS_USERSPACE_ATTR_PID" |
| ) |
| if self.get_attr("OVS_USERSPACE_ATTR_USERDATA") is not None: |
| print_str += "userdata=" |
| for f in self.get_attr("OVS_USERSPACE_ATTR_USERDATA"): |
| print_str += "%x." % f |
| if self.get_attr("OVS_USERSPACE_ATTR_EGRESS_TUN_PORT") is not None: |
| print_str += "egress_tun_port=%d" % self.get_attr( |
| "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT" |
| ) |
| print_str += ")" |
| return print_str |
| |
| def parse(self, actstr): |
| attrs_desc = ( |
| ("pid", "OVS_USERSPACE_ATTR_PID", int), |
| ("userdata", "OVS_USERSPACE_ATTR_USERDATA", |
| lambda x: list(bytearray.fromhex(x))), |
| ("egress_tun_port", "OVS_USERSPACE_ATTR_EGRESS_TUN_PORT", int) |
| ) |
| |
| attrs, actstr = parse_attrs(actstr, attrs_desc) |
| for attr in attrs: |
| self["attrs"].append(attr) |
| |
| return actstr |
| |
| def dpstr(self, more=False): |
| print_str = "" |
| |
| for field in self["attrs"]: |
| if field[1] == "none" or self.get_attr(field[0]) is None: |
| continue |
| if print_str != "": |
| print_str += "," |
| |
| if field[0] == "OVS_ACTION_ATTR_OUTPUT": |
| print_str += "%d" % int(self.get_attr(field[0])) |
| elif field[0] == "OVS_ACTION_ATTR_RECIRC": |
| print_str += "recirc(0x%x)" % int(self.get_attr(field[0])) |
| elif field[0] == "OVS_ACTION_ATTR_TRUNC": |
| print_str += "trunc(%d)" % int(self.get_attr(field[0])) |
| elif field[0] == "OVS_ACTION_ATTR_DROP": |
| print_str += "drop(%d)" % int(self.get_attr(field[0])) |
| elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR": |
| print_str += "ct_clear" |
| elif field[0] == "OVS_ACTION_ATTR_POP_VLAN": |
| print_str += "pop_vlan" |
| elif field[0] == "OVS_ACTION_ATTR_POP_ETH": |
| print_str += "pop_eth" |
| elif field[0] == "OVS_ACTION_ATTR_POP_NSH": |
| print_str += "pop_nsh" |
| elif field[0] == "OVS_ACTION_ATTR_POP_MPLS": |
| print_str += "pop_mpls" |
| else: |
| datum = self.get_attr(field[0]) |
| if field[0] == "OVS_ACTION_ATTR_CLONE": |
| print_str += "clone(" |
| print_str += datum.dpstr(more) |
| print_str += ")" |
| elif field[0] == "OVS_ACTION_ATTR_SET" or \ |
| field[0] == "OVS_ACTION_ATTR_SET_MASKED": |
| print_str += "set" |
| field = datum |
| mask = None |
| if field[0] == "OVS_ACTION_ATTR_SET_MASKED": |
| print_str += "_masked" |
| field = datum[0] |
| mask = datum[1] |
| print_str += "(" |
| print_str += field.dpstr(mask, more) |
| print_str += ")" |
| else: |
| try: |
| print_str += datum.dpstr(more) |
| except: |
| print_str += "{ATTR: %s not decoded}" % field[0] |
| |
| return print_str |
| |
| def parse(self, actstr): |
| totallen = len(actstr) |
| while len(actstr) != 0: |
| parsed = False |
| parencount = 0 |
| if actstr.startswith("drop"): |
| # If no reason is provided, the implicit drop is used (i.e no |
| # action). If some reason is given, an explicit action is used. |
| reason = None |
| if actstr.startswith("drop("): |
| parencount += 1 |
| |
| actstr, reason = parse_extract_field( |
| actstr, |
| "drop(", |
| r"([0-9]+)", |
| lambda x: int(x, 0), |
| False, |
| None, |
| ) |
| |
| if reason is not None: |
| self["attrs"].append(["OVS_ACTION_ATTR_DROP", reason]) |
| parsed = True |
| else: |
| actstr = actstr[len("drop"): ] |
| return (totallen - len(actstr)) |
| |
| elif parse_starts_block(actstr, r"^(\d+)", False, True): |
| actstr, output = parse_extract_field( |
| actstr, None, r"(\d+)", lambda x: int(x), False, "0" |
| ) |
| self["attrs"].append(["OVS_ACTION_ATTR_OUTPUT", output]) |
| parsed = True |
| elif parse_starts_block(actstr, "recirc(", False): |
| actstr, recircid = parse_extract_field( |
| actstr, |
| "recirc(", |
| r"([0-9a-fA-Fx]+)", |
| lambda x: int(x, 0), |
| False, |
| 0, |
| ) |
| parencount += 1 |
| self["attrs"].append(["OVS_ACTION_ATTR_RECIRC", recircid]) |
| parsed = True |
| |
| parse_flat_map = ( |
| ("ct_clear", "OVS_ACTION_ATTR_CT_CLEAR"), |
| ("pop_vlan", "OVS_ACTION_ATTR_POP_VLAN"), |
| ("pop_eth", "OVS_ACTION_ATTR_POP_ETH"), |
| ("pop_nsh", "OVS_ACTION_ATTR_POP_NSH"), |
| ) |
| |
| for flat_act in parse_flat_map: |
| if parse_starts_block(actstr, flat_act[0], False): |
| actstr = actstr[len(flat_act[0]):] |
| self["attrs"].append([flat_act[1], True]) |
| actstr = actstr[strspn(actstr, ", ") :] |
| parsed = True |
| |
| if parse_starts_block(actstr, "clone(", False): |
| parencount += 1 |
| subacts = ovsactions() |
| actstr = actstr[len("clone("):] |
| parsedLen = subacts.parse(actstr) |
| lst = [] |
| self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts)) |
| actstr = actstr[parsedLen:] |
| parsed = True |
| elif parse_starts_block(actstr, "set(", False): |
| parencount += 1 |
| k = ovskey() |
| actstr = actstr[len("set("):] |
| actstr = k.parse(actstr, None) |
| self["attrs"].append(("OVS_ACTION_ATTR_SET", k)) |
| if not actstr.startswith(")"): |
| actstr = ")" + actstr |
| parsed = True |
| elif parse_starts_block(actstr, "set_masked(", False): |
| parencount += 1 |
| k = ovskey() |
| m = ovskey() |
| actstr = actstr[len("set_masked("):] |
| actstr = k.parse(actstr, m) |
| self["attrs"].append(("OVS_ACTION_ATTR_SET_MASKED", [k, m])) |
| if not actstr.startswith(")"): |
| actstr = ")" + actstr |
| parsed = True |
| elif parse_starts_block(actstr, "ct(", False): |
| parencount += 1 |
| actstr = actstr[len("ct(") :] |
| ctact = ovsactions.ctact() |
| |
| for scan in ( |
| ("commit", "OVS_CT_ATTR_COMMIT", None), |
| ("force_commit", "OVS_CT_ATTR_FORCE_COMMIT", None), |
| ("zone", "OVS_CT_ATTR_ZONE", int), |
| ("mark", "OVS_CT_ATTR_MARK", int), |
| ("helper", "OVS_CT_ATTR_HELPER", lambda x, y: str(x)), |
| ("timeout", "OVS_CT_ATTR_TIMEOUT", lambda x, y: str(x)), |
| ): |
| if actstr.startswith(scan[0]): |
| actstr = actstr[len(scan[0]) :] |
| if scan[2] is not None: |
| if actstr[0] != "=": |
| raise ValueError("Invalid ct attr") |
| actstr = actstr[1:] |
| pos = strcspn(actstr, ",)") |
| datum = scan[2](actstr[:pos], 0) |
| ctact["attrs"].append([scan[1], datum]) |
| actstr = actstr[pos:] |
| else: |
| ctact["attrs"].append([scan[1], None]) |
| actstr = actstr[strspn(actstr, ", ") :] |
| # it seems strange to put this here, but nat() is a complex |
| # sub-action and this lets it sit anywhere in the ct() action |
| if actstr.startswith("nat"): |
| actstr = actstr[3:] |
| natact = ovsactions.ctact.natattr() |
| |
| if actstr.startswith("("): |
| parencount += 1 |
| t = None |
| actstr = actstr[1:] |
| if actstr.startswith("src"): |
| t = "OVS_NAT_ATTR_SRC" |
| actstr = actstr[3:] |
| elif actstr.startswith("dst"): |
| t = "OVS_NAT_ATTR_DST" |
| actstr = actstr[3:] |
| |
| actstr, ip_block_min = parse_extract_field( |
| actstr, "=", r"([0-9a-fA-F\.]+)", str, False |
| ) |
| actstr, ip_block_max = parse_extract_field( |
| actstr, "-", r"([0-9a-fA-F\.]+)", str, False |
| ) |
| |
| actstr, proto_min = parse_extract_field( |
| actstr, ":", r"(\d+)", int, False |
| ) |
| actstr, proto_max = parse_extract_field( |
| actstr, "-", r"(\d+)", int, False |
| ) |
| |
| if t is not None: |
| natact["attrs"].append([t, None]) |
| |
| if ip_block_min is not None: |
| natact["attrs"].append( |
| ["OVS_NAT_ATTR_IP_MIN", ip_block_min] |
| ) |
| if ip_block_max is not None: |
| natact["attrs"].append( |
| ["OVS_NAT_ATTR_IP_MAX", ip_block_max] |
| ) |
| if proto_min is not None: |
| natact["attrs"].append( |
| ["OVS_NAT_ATTR_PROTO_MIN", proto_min] |
| ) |
| if proto_max is not None: |
| natact["attrs"].append( |
| ["OVS_NAT_ATTR_PROTO_MAX", proto_max] |
| ) |
| |
| for natscan in ( |
| ("persistent", "OVS_NAT_ATTR_PERSISTENT"), |
| ("hash", "OVS_NAT_ATTR_PROTO_HASH"), |
| ("random", "OVS_NAT_ATTR_PROTO_RANDOM"), |
| ): |
| if actstr.startswith(natscan[0]): |
| actstr = actstr[len(natscan[0]) :] |
| natact["attrs"].append([natscan[1], None]) |
| actstr = actstr[strspn(actstr, ", ") :] |
| |
| ctact["attrs"].append(["OVS_CT_ATTR_NAT", natact]) |
| actstr = actstr[strspn(actstr, ", ") :] |
| |
| self["attrs"].append(["OVS_ACTION_ATTR_CT", ctact]) |
| parsed = True |
| |
| elif parse_starts_block(actstr, "sample(", False): |
| sampleact = self.sample() |
| actstr = sampleact.parse(actstr[len("sample(") : ]) |
| self["attrs"].append(["OVS_ACTION_ATTR_SAMPLE", sampleact]) |
| parsed = True |
| |
| elif parse_starts_block(actstr, "psample(", False): |
| psampleact = self.psample() |
| actstr = psampleact.parse(actstr[len("psample(") : ]) |
| self["attrs"].append(["OVS_ACTION_ATTR_PSAMPLE", psampleact]) |
| parsed = True |
| |
| elif parse_starts_block(actstr, "userspace(", False): |
| uact = self.userspace() |
| actstr = uact.parse(actstr[len("userspace(") : ]) |
| self["attrs"].append(["OVS_ACTION_ATTR_USERSPACE", uact]) |
| parsed = True |
| |
| elif parse_starts_block(actstr, "trunc(", False): |
| parencount += 1 |
| actstr, val = parse_extract_field( |
| actstr, |
| "trunc(", |
| r"([0-9]+)", |
| int, |
| False, |
| None, |
| ) |
| self["attrs"].append(["OVS_ACTION_ATTR_TRUNC", val]) |
| parsed = True |
| |
| actstr = actstr[strspn(actstr, ", ") :] |
| while parencount > 0: |
| parencount -= 1 |
| actstr = actstr[strspn(actstr, " "):] |
| if len(actstr) and actstr[0] != ")": |
| raise ValueError("Action str: '%s' unbalanced" % actstr) |
| actstr = actstr[1:] |
| |
| if len(actstr) and actstr[0] == ")": |
| return (totallen - len(actstr)) |
| |
| actstr = actstr[strspn(actstr, ", ") :] |
| |
| if not parsed: |
| raise ValueError("Action str: '%s' not supported" % actstr) |
| |
| return (totallen - len(actstr)) |
| |
| |
| class ovskey(nla): |
| nla_flags = NLA_F_NESTED |
| nla_map = ( |
| ("OVS_KEY_ATTR_UNSPEC", "none"), |
| ("OVS_KEY_ATTR_ENCAP", "none"), |
| ("OVS_KEY_ATTR_PRIORITY", "uint32"), |
| ("OVS_KEY_ATTR_IN_PORT", "uint32"), |
| ("OVS_KEY_ATTR_ETHERNET", "ethaddr"), |
| ("OVS_KEY_ATTR_VLAN", "uint16"), |
| ("OVS_KEY_ATTR_ETHERTYPE", "be16"), |
| ("OVS_KEY_ATTR_IPV4", "ovs_key_ipv4"), |
| ("OVS_KEY_ATTR_IPV6", "ovs_key_ipv6"), |
| ("OVS_KEY_ATTR_TCP", "ovs_key_tcp"), |
| ("OVS_KEY_ATTR_UDP", "ovs_key_udp"), |
| ("OVS_KEY_ATTR_ICMP", "ovs_key_icmp"), |
| ("OVS_KEY_ATTR_ICMPV6", "ovs_key_icmpv6"), |
| ("OVS_KEY_ATTR_ARP", "ovs_key_arp"), |
| ("OVS_KEY_ATTR_ND", "ovs_key_nd"), |
| ("OVS_KEY_ATTR_SKB_MARK", "uint32"), |
| ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"), |
| ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), |
| ("OVS_KEY_ATTR_TCP_FLAGS", "be16"), |
| ("OVS_KEY_ATTR_DP_HASH", "uint32"), |
| ("OVS_KEY_ATTR_RECIRC_ID", "uint32"), |
| ("OVS_KEY_ATTR_MPLS", "array(ovs_key_mpls)"), |
| ("OVS_KEY_ATTR_CT_STATE", "uint32"), |
| ("OVS_KEY_ATTR_CT_ZONE", "uint16"), |
| ("OVS_KEY_ATTR_CT_MARK", "uint32"), |
| ("OVS_KEY_ATTR_CT_LABELS", "none"), |
| ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", "ovs_key_ct_tuple_ipv4"), |
| ("OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", "ovs_key_ct_tuple_ipv6"), |
| ("OVS_KEY_ATTR_NSH", "none"), |
| ("OVS_KEY_ATTR_PACKET_TYPE", "none"), |
| ("OVS_KEY_ATTR_ND_EXTENSIONS", "none"), |
| ("OVS_KEY_ATTR_TUNNEL_INFO", "none"), |
| ("OVS_KEY_ATTR_IPV6_EXTENSIONS", "none"), |
| ) |
| |
| class ovs_key_proto(nla): |
| fields = ( |
| ("src", "!H"), |
| ("dst", "!H"), |
| ) |
| |
| fields_map = ( |
| ("src", "src", "%d", lambda x: int(x) if x else 0, |
| convert_int(16)), |
| ("dst", "dst", "%d", lambda x: int(x) if x else 0, |
| convert_int(16)), |
| ) |
| |
| def __init__( |
| self, |
| protostr, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| self.proto_str = protostr |
| nla.__init__( |
| self, |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| def parse(self, flowstr, typeInst): |
| if not flowstr.startswith(self.proto_str): |
| return None, None |
| |
| k = typeInst() |
| m = typeInst() |
| |
| flowstr = flowstr[len(self.proto_str) :] |
| if flowstr.startswith("("): |
| flowstr = flowstr[1:] |
| |
| keybits = b"" |
| maskbits = b"" |
| for f in self.fields_map: |
| if flowstr.startswith(f[1]): |
| # the following assumes that the field looks |
| # something like 'field.' where '.' is a |
| # character that we don't exactly care about. |
| flowstr = flowstr[len(f[1]) + 1 :] |
| splitchar = 0 |
| for c in flowstr: |
| if c == "," or c == ")": |
| break |
| splitchar += 1 |
| data = flowstr[:splitchar] |
| flowstr = flowstr[splitchar:] |
| else: |
| data = "" |
| |
| if len(f) > 4: |
| k[f[0]], m[f[0]] = f[4](data) |
| else: |
| k[f[0]] = f[3](data) |
| m[f[0]] = f[3](data) |
| |
| flowstr = flowstr[strspn(flowstr, ", ") :] |
| if len(flowstr) == 0: |
| return flowstr, k, m |
| |
| flowstr = flowstr[strspn(flowstr, "), ") :] |
| |
| return flowstr, k, m |
| |
| def dpstr(self, masked=None, more=False): |
| outstr = self.proto_str + "(" |
| first = False |
| for f in self.fields_map: |
| if first: |
| outstr += "," |
| if masked is None: |
| outstr += "%s=" % f[0] |
| if isinstance(f[2], str): |
| outstr += f[2] % self[f[1]] |
| else: |
| outstr += f[2](self[f[1]]) |
| first = True |
| elif more or f[3](masked[f[1]]) != 0: |
| outstr += "%s=" % f[0] |
| if isinstance(f[2], str): |
| outstr += f[2] % self[f[1]] |
| else: |
| outstr += f[2](self[f[1]]) |
| outstr += "/" |
| if isinstance(f[2], str): |
| outstr += f[2] % masked[f[1]] |
| else: |
| outstr += f[2](masked[f[1]]) |
| first = True |
| outstr += ")" |
| return outstr |
| |
| class ethaddr(ovs_key_proto): |
| fields = ( |
| ("src", "!6s"), |
| ("dst", "!6s"), |
| ) |
| |
| fields_map = ( |
| ( |
| "src", |
| "src", |
| macstr, |
| lambda x: int.from_bytes(x, "big"), |
| convert_mac, |
| ), |
| ( |
| "dst", |
| "dst", |
| macstr, |
| lambda x: int.from_bytes(x, "big"), |
| convert_mac, |
| ), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "eth", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_ipv4(ovs_key_proto): |
| fields = ( |
| ("src", "!I"), |
| ("dst", "!I"), |
| ("proto", "B"), |
| ("tos", "B"), |
| ("ttl", "B"), |
| ("frag", "B"), |
| ) |
| |
| fields_map = ( |
| ( |
| "src", |
| "src", |
| lambda x: str(ipaddress.IPv4Address(x)), |
| int, |
| convert_ipv4, |
| ), |
| ( |
| "dst", |
| "dst", |
| lambda x: str(ipaddress.IPv4Address(x)), |
| int, |
| convert_ipv4, |
| ), |
| ("proto", "proto", "%d", lambda x: int(x) if x else 0, |
| convert_int(8)), |
| ("tos", "tos", "%d", lambda x: int(x) if x else 0, |
| convert_int(8)), |
| ("ttl", "ttl", "%d", lambda x: int(x) if x else 0, |
| convert_int(8)), |
| ("frag", "frag", "%d", lambda x: int(x) if x else 0, |
| convert_int(8)), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "ipv4", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_ipv6(ovs_key_proto): |
| fields = ( |
| ("src", "!16s"), |
| ("dst", "!16s"), |
| ("label", "!I"), |
| ("proto", "B"), |
| ("tclass", "B"), |
| ("hlimit", "B"), |
| ("frag", "B"), |
| ) |
| |
| fields_map = ( |
| ( |
| "src", |
| "src", |
| lambda x: str(ipaddress.IPv6Address(x)), |
| lambda x: ipaddress.IPv6Address(x).packed if x else 0, |
| convert_ipv6, |
| ), |
| ( |
| "dst", |
| "dst", |
| lambda x: str(ipaddress.IPv6Address(x)), |
| lambda x: ipaddress.IPv6Address(x).packed if x else 0, |
| convert_ipv6, |
| ), |
| ("label", "label", "%d", lambda x: int(x) if x else 0), |
| ("proto", "proto", "%d", lambda x: int(x) if x else 0), |
| ("tclass", "tclass", "%d", lambda x: int(x) if x else 0), |
| ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0), |
| ("frag", "frag", "%d", lambda x: int(x) if x else 0), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "ipv6", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_tcp(ovs_key_proto): |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "tcp", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_udp(ovs_key_proto): |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "udp", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_sctp(ovs_key_proto): |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "sctp", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_icmp(ovs_key_proto): |
| fields = ( |
| ("type", "B"), |
| ("code", "B"), |
| ) |
| |
| fields_map = ( |
| ("type", "type", "%d", lambda x: int(x) if x else 0), |
| ("code", "code", "%d", lambda x: int(x) if x else 0), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "icmp", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_icmpv6(ovs_key_icmp): |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "icmpv6", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_arp(ovs_key_proto): |
| fields = ( |
| ("sip", "!I"), |
| ("tip", "!I"), |
| ("op", "!H"), |
| ("sha", "!6s"), |
| ("tha", "!6s"), |
| ("pad", "xx"), |
| ) |
| |
| fields_map = ( |
| ( |
| "sip", |
| "sip", |
| lambda x: str(ipaddress.IPv4Address(x)), |
| int, |
| convert_ipv4, |
| ), |
| ( |
| "tip", |
| "tip", |
| lambda x: str(ipaddress.IPv4Address(x)), |
| int, |
| convert_ipv4, |
| ), |
| ("op", "op", "%d", lambda x: int(x) if x else 0), |
| ( |
| "sha", |
| "sha", |
| macstr, |
| lambda x: int.from_bytes(x, "big"), |
| convert_mac, |
| ), |
| ( |
| "tha", |
| "tha", |
| macstr, |
| lambda x: int.from_bytes(x, "big"), |
| convert_mac, |
| ), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "arp", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_nd(ovs_key_proto): |
| fields = ( |
| ("target", "!16s"), |
| ("sll", "!6s"), |
| ("tll", "!6s"), |
| ) |
| |
| fields_map = ( |
| ( |
| "target", |
| "target", |
| lambda x: str(ipaddress.IPv6Address(x)), |
| convert_ipv6, |
| ), |
| ("sll", "sll", macstr, lambda x: int.from_bytes(x, "big")), |
| ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "nd", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_ct_tuple_ipv4(ovs_key_proto): |
| fields = ( |
| ("src", "!I"), |
| ("dst", "!I"), |
| ("tp_src", "!H"), |
| ("tp_dst", "!H"), |
| ("proto", "B"), |
| ) |
| |
| fields_map = ( |
| ( |
| "src", |
| "src", |
| lambda x: str(ipaddress.IPv4Address(x)), |
| int, |
| convert_ipv4, |
| ), |
| ( |
| "dst", |
| "dst", |
| lambda x: str(ipaddress.IPv4Address(x)), |
| int, |
| convert_ipv4, |
| ), |
| ("tp_src", "tp_src", "%d", int), |
| ("tp_dst", "tp_dst", "%d", int), |
| ("proto", "proto", "%d", int), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "ct_tuple4", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_ct_tuple_ipv6(nla): |
| fields = ( |
| ("src", "!16s"), |
| ("dst", "!16s"), |
| ("tp_src", "!H"), |
| ("tp_dst", "!H"), |
| ("proto", "B"), |
| ) |
| |
| fields_map = ( |
| ( |
| "src", |
| "src", |
| lambda x: str(ipaddress.IPv6Address(x)), |
| convert_ipv6, |
| ), |
| ( |
| "dst", |
| "dst", |
| lambda x: str(ipaddress.IPv6Address(x)), |
| convert_ipv6, |
| ), |
| ("tp_src", "tp_src", "%d", int), |
| ("tp_dst", "tp_dst", "%d", int), |
| ("proto", "proto", "%d", int), |
| ) |
| |
| def __init__( |
| self, |
| data=None, |
| offset=None, |
| parent=None, |
| length=None, |
| init=None, |
| ): |
| ovskey.ovs_key_proto.__init__( |
| self, |
| "ct_tuple6", |
| data=data, |
| offset=offset, |
| parent=parent, |
| length=length, |
| init=init, |
| ) |
| |
| class ovs_key_tunnel(nla): |
| nla_flags = NLA_F_NESTED |
| |
| nla_map = ( |
| ("OVS_TUNNEL_KEY_ATTR_ID", "be64"), |
| ("OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "ipaddr"), |
| ("OVS_TUNNEL_KEY_ATTR_IPV4_DST", "ipaddr"), |
| ("OVS_TUNNEL_KEY_ATTR_TOS", "uint8"), |
| ("OVS_TUNNEL_KEY_ATTR_TTL", "uint8"), |
| ("OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", "flag"), |
| ("OVS_TUNNEL_KEY_ATTR_CSUM", "flag"), |
| ("OVS_TUNNEL_KEY_ATTR_OAM", "flag"), |
| ("OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS", "array(uint32)"), |
| ("OVS_TUNNEL_KEY_ATTR_TP_SRC", "be16"), |
| ("OVS_TUNNEL_KEY_ATTR_TP_DST", "be16"), |
| ("OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS", "none"), |
| ("OVS_TUNNEL_KEY_ATTR_IPV6_SRC", "ipaddr"), |
| ("OVS_TUNNEL_KEY_ATTR_IPV6_DST", "ipaddr"), |
| ("OVS_TUNNEL_KEY_ATTR_PAD", "none"), |
| ("OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS", "none"), |
| ("OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE", "flag"), |
| ) |
| |
| def parse(self, flowstr, mask=None): |
| if not flowstr.startswith("tunnel("): |
| return None, None |
| |
| k = ovskey.ovs_key_tunnel() |
| if mask is not None: |
| mask = ovskey.ovs_key_tunnel() |
| |
| flowstr = flowstr[len("tunnel("):] |
| |
| v6_address = None |
| |
| fields = [ |
| ("tun_id=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_ID", |
| 0xffffffffffffffff, None, None), |
| |
| ("src=", r"([0-9a-fA-F\.]+)", str, |
| "OVS_TUNNEL_KEY_ATTR_IPV4_SRC", "255.255.255.255", "0.0.0.0", |
| False), |
| ("dst=", r"([0-9a-fA-F\.]+)", str, |
| "OVS_TUNNEL_KEY_ATTR_IPV4_DST", "255.255.255.255", "0.0.0.0", |
| False), |
| |
| ("ipv6_src=", r"([0-9a-fA-F:]+)", str, |
| "OVS_TUNNEL_KEY_ATTR_IPV6_SRC", |
| "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), |
| ("ipv6_dst=", r"([0-9a-fA-F:]+)", str, |
| "OVS_TUNNEL_KEY_ATTR_IPV6_DST", |
| "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", True), |
| |
| ("tos=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TOS", 255, 0, |
| None), |
| ("ttl=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TTL", 255, 0, |
| None), |
| |
| ("tp_src=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_SRC", |
| 65535, 0, None), |
| ("tp_dst=", r"(\d+)", int, "OVS_TUNNEL_KEY_ATTR_TP_DST", |
| 65535, 0, None), |
| ] |
| |
| forced_include = ["OVS_TUNNEL_KEY_ATTR_TTL"] |
| |
| for prefix, regex, typ, attr_name, mask_val, default_val, v46_flag in fields: |
| flowstr, value = parse_extract_field(flowstr, prefix, regex, typ, False) |
| if not attr_name: |
| raise Exception("Bad list value in tunnel fields") |
| |
| if value is None and attr_name in forced_include: |
| value = default_val |
| mask_val = default_val |
| |
| if value is not None: |
| if v46_flag is not None: |
| if v6_address is None: |
| v6_address = v46_flag |
| if v46_flag != v6_address: |
| raise ValueError("Cannot mix v6 and v4 addresses") |
| k["attrs"].append([attr_name, value]) |
| if mask is not None: |
| mask["attrs"].append([attr_name, mask_val]) |
| else: |
| if v46_flag is not None: |
| if v6_address is None or v46_flag != v6_address: |
| continue |
| if mask is not None: |
| mask["attrs"].append([attr_name, default_val]) |
| |
| if k["attrs"][0][0] != "OVS_TUNNEL_KEY_ATTR_ID": |
| raise ValueError("Needs a tunid set") |
| |
| if flowstr.startswith("flags("): |
| flowstr = flowstr[len("flags("):] |
| flagspos = flowstr.find(")") |
| flags = flowstr[:flagspos] |
| flowstr = flowstr[flagspos + 1:] |
| |
| flag_attrs = { |
| "df": "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT", |
| "csum": "OVS_TUNNEL_KEY_ATTR_CSUM", |
| "oam": "OVS_TUNNEL_KEY_ATTR_OAM" |
| } |
| |
| for flag in flags.split("|"): |
| if flag in flag_attrs: |
| k["attrs"].append([flag_attrs[flag], True]) |
| if mask is not None: |
| mask["attrs"].append([flag_attrs[flag], True]) |
| |
| flowstr = flowstr[strspn(flowstr, ", ") :] |
| return flowstr, k, mask |
| |
| def dpstr(self, mask=None, more=False): |
| print_str = "tunnel(" |
| |
| flagsattrs = [] |
| for k in self["attrs"]: |
| noprint = False |
| if k[0] == "OVS_TUNNEL_KEY_ATTR_ID": |
| print_str += "tun_id=%d" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_SRC": |
| print_str += "src=%s" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV4_DST": |
| print_str += "dst=%s" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_SRC": |
| print_str += "ipv6_src=%s" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_IPV6_DST": |
| print_str += "ipv6_dst=%s" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_TOS": |
| print_str += "tos=%d" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_TTL": |
| print_str += "ttl=%d" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_SRC": |
| print_str += "tp_src=%d" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_TP_DST": |
| print_str += "tp_dst=%d" % k[1] |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT": |
| noprint = True |
| flagsattrs.append("df") |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_CSUM": |
| noprint = True |
| flagsattrs.append("csum") |
| elif k[0] == "OVS_TUNNEL_KEY_ATTR_OAM": |
| noprint = True |
| flagsattrs.append("oam") |
| |
| if not noprint: |
| print_str += "," |
| |
| if len(flagsattrs): |
| print_str += "flags(" + "|".join(flagsattrs) + ")" |
| print_str += ")" |
| return print_str |
| |
| class ovs_key_mpls(nla): |
| fields = (("lse", ">I"),) |
| |
| def parse(self, flowstr, mask=None): |
| for field in ( |
| ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse), |
| ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse), |
| ("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", intparse), |
| ("OVS_KEY_ATTR_TUNNEL", "tunnel", ovskey.ovs_key_tunnel), |
| ("OVS_KEY_ATTR_DP_HASH", "dp_hash", intparse), |
| ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state), |
| ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse), |
| ("OVS_KEY_ATTR_CT_MARK", "ct_mark", intparse), |
| ("OVS_KEY_ATTR_IN_PORT", "in_port", intparse), |
| ( |
| "OVS_KEY_ATTR_ETHERNET", |
| "eth", |
| ovskey.ethaddr, |
| ), |
| ( |
| "OVS_KEY_ATTR_ETHERTYPE", |
| "eth_type", |
| lambda x: intparse(x, "0xffff"), |
| ), |
| ( |
| "OVS_KEY_ATTR_IPV4", |
| "ipv4", |
| ovskey.ovs_key_ipv4, |
| ), |
| ( |
| "OVS_KEY_ATTR_IPV6", |
| "ipv6", |
| ovskey.ovs_key_ipv6, |
| ), |
| ( |
| "OVS_KEY_ATTR_ARP", |
| "arp", |
| ovskey.ovs_key_arp, |
| ), |
| ( |
| "OVS_KEY_ATTR_TCP", |
| "tcp", |
| ovskey.ovs_key_tcp, |
| ), |
| ( |
| "OVS_KEY_ATTR_UDP", |
| "udp", |
| ovskey.ovs_key_udp, |
| ), |
| ( |
| "OVS_KEY_ATTR_ICMP", |
| "icmp", |
| ovskey.ovs_key_icmp, |
| ), |
| ( |
| "OVS_KEY_ATTR_TCP_FLAGS", |
| "tcp_flags", |
| lambda x: parse_flags(x, None), |
| ), |
| ): |
| fld = field[1] + "(" |
| if not flowstr.startswith(fld): |
| continue |
| |
| if not isinstance(field[2], types.FunctionType): |
| nk = field[2]() |
| flowstr, k, m = nk.parse(flowstr, field[2]) |
| else: |
| flowstr = flowstr[len(fld) :] |
| flowstr, k, m = field[2](flowstr) |
| |
| if m and mask is not None: |
| mask["attrs"].append([field[0], m]) |
| self["attrs"].append([field[0], k]) |
| |
| flowstr = flowstr[strspn(flowstr, "), ") :] |
| |
| return flowstr |
| |
| def dpstr(self, mask=None, more=False): |
| print_str = "" |
| |
| for field in ( |
| ( |
| "OVS_KEY_ATTR_PRIORITY", |
| "skb_priority", |
| "%d", |
| lambda x: False, |
| True, |
| ), |
| ( |
| "OVS_KEY_ATTR_SKB_MARK", |
| "skb_mark", |
| "%d", |
| lambda x: False, |
| True, |
| ), |
| ( |
| "OVS_KEY_ATTR_RECIRC_ID", |
| "recirc_id", |
| "0x%08X", |
| lambda x: False, |
| True, |
| ), |
| ( |
| "OVS_KEY_ATTR_DP_HASH", |
| "dp_hash", |
| "0x%08X", |
| lambda x: False, |
| True, |
| ), |
| ( |
| "OVS_KEY_ATTR_TUNNEL", |
| "tunnel", |
| None, |
| False, |
| False, |
| ), |
| ( |
| "OVS_KEY_ATTR_CT_STATE", |
| "ct_state", |
| "0x%04x", |
| lambda x: False, |
| True, |
| ), |
| ( |
| "OVS_KEY_ATTR_CT_ZONE", |
| "ct_zone", |
| "0x%04x", |
| lambda x: False, |
| True, |
| ), |
| ( |
| "OVS_KEY_ATTR_CT_MARK", |
| "ct_mark", |
| "0x%08x", |
| lambda x: False, |
| True, |
| ), |
| ( |
| "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4", |
| None, |
| None, |
| False, |
| False, |
| ), |
| ( |
| "OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6", |
| None, |
| None, |
| False, |
| False, |
| ), |
| ( |
| "OVS_KEY_ATTR_IN_PORT", |
| "in_port", |
| "%d", |
| lambda x: True, |
| True, |
| ), |
| ("OVS_KEY_ATTR_ETHERNET", None, None, False, False), |
| ( |
| "OVS_KEY_ATTR_ETHERTYPE", |
| "eth_type", |
| "0x%04x", |
| lambda x: int(x) == 0xFFFF, |
| True, |
| ), |
| ("OVS_KEY_ATTR_IPV4", None, None, False, False), |
| ("OVS_KEY_ATTR_IPV6", None, None, False, False), |
| ("OVS_KEY_ATTR_ARP", None, None, False, False), |
| ("OVS_KEY_ATTR_TCP", None, None, False, False), |
| ( |
| "OVS_KEY_ATTR_TCP_FLAGS", |
| "tcp_flags", |
| "0x%04x", |
| lambda x: False, |
| True, |
| ), |
| ("OVS_KEY_ATTR_UDP", None, None, False, False), |
| ("OVS_KEY_ATTR_SCTP", None, None, False, False), |
| ("OVS_KEY_ATTR_ICMP", None, None, False, False), |
| ("OVS_KEY_ATTR_ICMPV6", None, None, False, False), |
| ("OVS_KEY_ATTR_ND", None, None, False, False), |
| ): |
| v = self.get_attr(field[0]) |
| if v is not None: |
| m = None if mask is None else mask.get_attr(field[0]) |
| if field[4] is False: |
| print_str += v.dpstr(m, more) |
| print_str += "," |
| else: |
| if m is None or field[3](m): |
| print_str += field[1] + "(" |
| print_str += field[2] % v |
| print_str += ")," |
| elif more or m != 0: |
| print_str += field[1] + "(" |
| print_str += (field[2] % v) + "/" + (field[2] % m) |
| print_str += ")," |
| |
| return print_str |
| |
| |
| class OvsPacket(GenericNetlinkSocket): |
| OVS_PACKET_CMD_MISS = 1 # Flow table miss |
| OVS_PACKET_CMD_ACTION = 2 # USERSPACE action |
| OVS_PACKET_CMD_EXECUTE = 3 # Apply actions to packet |
| |
| class ovs_packet_msg(ovs_dp_msg): |
| nla_map = ( |
| ("OVS_PACKET_ATTR_UNSPEC", "none"), |
| ("OVS_PACKET_ATTR_PACKET", "array(uint8)"), |
| ("OVS_PACKET_ATTR_KEY", "ovskey"), |
| ("OVS_PACKET_ATTR_ACTIONS", "ovsactions"), |
| ("OVS_PACKET_ATTR_USERDATA", "none"), |
| ("OVS_PACKET_ATTR_EGRESS_TUN_KEY", "none"), |
| ("OVS_PACKET_ATTR_UNUSED1", "none"), |
| ("OVS_PACKET_ATTR_UNUSED2", "none"), |
| ("OVS_PACKET_ATTR_PROBE", "none"), |
| ("OVS_PACKET_ATTR_MRU", "uint16"), |
| ("OVS_PACKET_ATTR_LEN", "uint32"), |
| ("OVS_PACKET_ATTR_HASH", "uint64"), |
| ) |
| |
| def __init__(self): |
| GenericNetlinkSocket.__init__(self) |
| self.bind(OVS_PACKET_FAMILY, OvsPacket.ovs_packet_msg) |
| |
| def upcall_handler(self, up=None): |
| print("listening on upcall packet handler:", self.epid) |
| while True: |
| try: |
| msgs = self.get() |
| for msg in msgs: |
| if not up: |
| continue |
| if msg["cmd"] == OvsPacket.OVS_PACKET_CMD_MISS: |
| up.miss(msg) |
| elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_ACTION: |
| up.action(msg) |
| elif msg["cmd"] == OvsPacket.OVS_PACKET_CMD_EXECUTE: |
| up.execute(msg) |
| else: |
| print("Unkonwn cmd: %d" % msg["cmd"]) |
| except NetlinkError as ne: |
| raise ne |
| |
| |
| class OvsDatapath(GenericNetlinkSocket): |
| OVS_DP_F_VPORT_PIDS = 1 << 1 |
| OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3 |
| |
| class dp_cmd_msg(ovs_dp_msg): |
| """ |
| Message class that will be used to communicate with the kernel module. |
| """ |
| |
| nla_map = ( |
| ("OVS_DP_ATTR_UNSPEC", "none"), |
| ("OVS_DP_ATTR_NAME", "asciiz"), |
| ("OVS_DP_ATTR_UPCALL_PID", "array(uint32)"), |
| ("OVS_DP_ATTR_STATS", "dpstats"), |
| ("OVS_DP_ATTR_MEGAFLOW_STATS", "megaflowstats"), |
| ("OVS_DP_ATTR_USER_FEATURES", "uint32"), |
| ("OVS_DP_ATTR_PAD", "none"), |
| ("OVS_DP_ATTR_MASKS_CACHE_SIZE", "uint32"), |
| ("OVS_DP_ATTR_PER_CPU_PIDS", "array(uint32)"), |
| ) |
| |
| class dpstats(nla): |
| fields = ( |
| ("hit", "=Q"), |
| ("missed", "=Q"), |
| ("lost", "=Q"), |
| ("flows", "=Q"), |
| ) |
| |
| class megaflowstats(nla): |
| fields = ( |
| ("mask_hit", "=Q"), |
| ("masks", "=I"), |
| ("padding", "=I"), |
| ("cache_hits", "=Q"), |
| ("pad1", "=Q"), |
| ) |
| |
| def __init__(self): |
| GenericNetlinkSocket.__init__(self) |
| self.bind(OVS_DATAPATH_FAMILY, OvsDatapath.dp_cmd_msg) |
| |
| def info(self, dpname, ifindex=0): |
| msg = OvsDatapath.dp_cmd_msg() |
| msg["cmd"] = OVS_DP_CMD_GET |
| msg["version"] = OVS_DATAPATH_VERSION |
| msg["reserved"] = 0 |
| msg["dpifindex"] = ifindex |
| msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) |
| |
| try: |
| reply = self.nlm_request( |
| msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| if ne.code == errno.ENODEV: |
| reply = None |
| else: |
| raise ne |
| |
| return reply |
| |
| def create( |
| self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket() |
| ): |
| msg = OvsDatapath.dp_cmd_msg() |
| msg["cmd"] = OVS_DP_CMD_NEW |
| if versionStr is None: |
| msg["version"] = OVS_DATAPATH_VERSION |
| else: |
| msg["version"] = int(versionStr.split(":")[0], 0) |
| msg["reserved"] = 0 |
| msg["dpifindex"] = 0 |
| msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) |
| |
| dpfeatures = 0 |
| if versionStr is not None and versionStr.find(":") != -1: |
| dpfeatures = int(versionStr.split(":")[1], 0) |
| else: |
| if versionStr is None or versionStr.find(":") == -1: |
| dpfeatures |= OvsDatapath.OVS_DP_F_DISPATCH_UPCALL_PER_CPU |
| dpfeatures &= ~OvsDatapath.OVS_DP_F_VPORT_PIDS |
| |
| nproc = multiprocessing.cpu_count() |
| procarray = [] |
| for i in range(1, nproc): |
| procarray += [int(p.epid)] |
| msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", procarray]) |
| msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures]) |
| if not shouldUpcall: |
| msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]]) |
| |
| try: |
| reply = self.nlm_request( |
| msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| if ne.code == errno.EEXIST: |
| reply = None |
| else: |
| raise ne |
| |
| return reply |
| |
| def destroy(self, dpname): |
| msg = OvsDatapath.dp_cmd_msg() |
| msg["cmd"] = OVS_DP_CMD_DEL |
| msg["version"] = OVS_DATAPATH_VERSION |
| msg["reserved"] = 0 |
| msg["dpifindex"] = 0 |
| msg["attrs"].append(["OVS_DP_ATTR_NAME", dpname]) |
| |
| try: |
| reply = self.nlm_request( |
| msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| if ne.code == errno.ENODEV: |
| reply = None |
| else: |
| raise ne |
| |
| return reply |
| |
| |
| class OvsVport(GenericNetlinkSocket): |
| OVS_VPORT_TYPE_NETDEV = 1 |
| OVS_VPORT_TYPE_INTERNAL = 2 |
| OVS_VPORT_TYPE_GRE = 3 |
| OVS_VPORT_TYPE_VXLAN = 4 |
| OVS_VPORT_TYPE_GENEVE = 5 |
| |
| class ovs_vport_msg(ovs_dp_msg): |
| nla_map = ( |
| ("OVS_VPORT_ATTR_UNSPEC", "none"), |
| ("OVS_VPORT_ATTR_PORT_NO", "uint32"), |
| ("OVS_VPORT_ATTR_TYPE", "uint32"), |
| ("OVS_VPORT_ATTR_NAME", "asciiz"), |
| ("OVS_VPORT_ATTR_OPTIONS", "vportopts"), |
| ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"), |
| ("OVS_VPORT_ATTR_STATS", "vportstats"), |
| ("OVS_VPORT_ATTR_PAD", "none"), |
| ("OVS_VPORT_ATTR_IFINDEX", "uint32"), |
| ("OVS_VPORT_ATTR_NETNSID", "uint32"), |
| ) |
| |
| class vportopts(nla): |
| nla_map = ( |
| ("OVS_TUNNEL_ATTR_UNSPEC", "none"), |
| ("OVS_TUNNEL_ATTR_DST_PORT", "uint16"), |
| ("OVS_TUNNEL_ATTR_EXTENSION", "none"), |
| ) |
| |
| class vportstats(nla): |
| fields = ( |
| ("rx_packets", "=Q"), |
| ("tx_packets", "=Q"), |
| ("rx_bytes", "=Q"), |
| ("tx_bytes", "=Q"), |
| ("rx_errors", "=Q"), |
| ("tx_errors", "=Q"), |
| ("rx_dropped", "=Q"), |
| ("tx_dropped", "=Q"), |
| ) |
| |
| def type_to_str(vport_type): |
| if vport_type == OvsVport.OVS_VPORT_TYPE_NETDEV: |
| return "netdev" |
| elif vport_type == OvsVport.OVS_VPORT_TYPE_INTERNAL: |
| return "internal" |
| elif vport_type == OvsVport.OVS_VPORT_TYPE_GRE: |
| return "gre" |
| elif vport_type == OvsVport.OVS_VPORT_TYPE_VXLAN: |
| return "vxlan" |
| elif vport_type == OvsVport.OVS_VPORT_TYPE_GENEVE: |
| return "geneve" |
| raise ValueError("Unknown vport type:%d" % vport_type) |
| |
| def str_to_type(vport_type): |
| if vport_type == "netdev": |
| return OvsVport.OVS_VPORT_TYPE_NETDEV |
| elif vport_type == "internal": |
| return OvsVport.OVS_VPORT_TYPE_INTERNAL |
| elif vport_type == "gre": |
| return OvsVport.OVS_VPORT_TYPE_INTERNAL |
| elif vport_type == "vxlan": |
| return OvsVport.OVS_VPORT_TYPE_VXLAN |
| elif vport_type == "geneve": |
| return OvsVport.OVS_VPORT_TYPE_GENEVE |
| raise ValueError("Unknown vport type: '%s'" % vport_type) |
| |
| def __init__(self, packet=OvsPacket()): |
| GenericNetlinkSocket.__init__(self) |
| self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg) |
| self.upcall_packet = packet |
| |
| def info(self, vport_name, dpifindex=0, portno=None): |
| msg = OvsVport.ovs_vport_msg() |
| |
| msg["cmd"] = OVS_VPORT_CMD_GET |
| msg["version"] = OVS_DATAPATH_VERSION |
| msg["reserved"] = 0 |
| msg["dpifindex"] = dpifindex |
| |
| if portno is None: |
| msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_name]) |
| else: |
| msg["attrs"].append(["OVS_VPORT_ATTR_PORT_NO", portno]) |
| |
| try: |
| reply = self.nlm_request( |
| msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| if ne.code == errno.ENODEV: |
| reply = None |
| else: |
| raise ne |
| return reply |
| |
| def attach(self, dpindex, vport_ifname, ptype, dport, lwt): |
| msg = OvsVport.ovs_vport_msg() |
| |
| msg["cmd"] = OVS_VPORT_CMD_NEW |
| msg["version"] = OVS_DATAPATH_VERSION |
| msg["reserved"] = 0 |
| msg["dpifindex"] = dpindex |
| port_type = OvsVport.str_to_type(ptype) |
| |
| msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) |
| msg["attrs"].append( |
| ["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]] |
| ) |
| |
| TUNNEL_DEFAULTS = [("geneve", 6081), |
| ("vxlan", 4789)] |
| |
| for tnl in TUNNEL_DEFAULTS: |
| if ptype == tnl[0]: |
| if not dport: |
| dport = tnl[1] |
| |
| if not lwt: |
| vportopt = OvsVport.ovs_vport_msg.vportopts() |
| vportopt["attrs"].append( |
| ["OVS_TUNNEL_ATTR_DST_PORT", socket.htons(dport)] |
| ) |
| msg["attrs"].append( |
| ["OVS_VPORT_ATTR_OPTIONS", vportopt] |
| ) |
| else: |
| port_type = OvsVport.OVS_VPORT_TYPE_NETDEV |
| ipr = pyroute2.iproute.IPRoute() |
| |
| if tnl[0] == "geneve": |
| ipr.link("add", ifname=vport_ifname, kind=tnl[0], |
| geneve_port=dport, |
| geneve_collect_metadata=True, |
| geneve_udp_zero_csum6_rx=1) |
| elif tnl[0] == "vxlan": |
| ipr.link("add", ifname=vport_ifname, kind=tnl[0], |
| vxlan_learning=0, vxlan_collect_metadata=1, |
| vxlan_udp_zero_csum6_rx=1, vxlan_port=dport) |
| break |
| msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type]) |
| |
| try: |
| reply = self.nlm_request( |
| msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| if ne.code == errno.EEXIST: |
| reply = None |
| else: |
| raise ne |
| return reply |
| |
| def reset_upcall(self, dpindex, vport_ifname, p=None): |
| msg = OvsVport.ovs_vport_msg() |
| |
| msg["cmd"] = OVS_VPORT_CMD_SET |
| msg["version"] = OVS_DATAPATH_VERSION |
| msg["reserved"] = 0 |
| msg["dpifindex"] = dpindex |
| msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) |
| |
| if p == None: |
| p = self.upcall_packet |
| else: |
| self.upcall_packet = p |
| |
| msg["attrs"].append(["OVS_VPORT_ATTR_UPCALL_PID", [p.epid]]) |
| |
| try: |
| reply = self.nlm_request( |
| msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| raise ne |
| return reply |
| |
| def detach(self, dpindex, vport_ifname): |
| msg = OvsVport.ovs_vport_msg() |
| |
| msg["cmd"] = OVS_VPORT_CMD_DEL |
| msg["version"] = OVS_DATAPATH_VERSION |
| msg["reserved"] = 0 |
| msg["dpifindex"] = dpindex |
| msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname]) |
| |
| try: |
| reply = self.nlm_request( |
| msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| if ne.code == errno.ENODEV: |
| reply = None |
| else: |
| raise ne |
| return reply |
| |
| def upcall_handler(self, handler=None): |
| self.upcall_packet.upcall_handler(handler) |
| |
| |
| class OvsFlow(GenericNetlinkSocket): |
| class ovs_flow_msg(ovs_dp_msg): |
| nla_map = ( |
| ("OVS_FLOW_ATTR_UNSPEC", "none"), |
| ("OVS_FLOW_ATTR_KEY", "ovskey"), |
| ("OVS_FLOW_ATTR_ACTIONS", "ovsactions"), |
| ("OVS_FLOW_ATTR_STATS", "flowstats"), |
| ("OVS_FLOW_ATTR_TCP_FLAGS", "uint8"), |
| ("OVS_FLOW_ATTR_USED", "uint64"), |
| ("OVS_FLOW_ATTR_CLEAR", "none"), |
| ("OVS_FLOW_ATTR_MASK", "ovskey"), |
| ("OVS_FLOW_ATTR_PROBE", "none"), |
| ("OVS_FLOW_ATTR_UFID", "array(uint32)"), |
| ("OVS_FLOW_ATTR_UFID_FLAGS", "uint32"), |
| ) |
| |
| class flowstats(nla): |
| fields = ( |
| ("packets", "=Q"), |
| ("bytes", "=Q"), |
| ) |
| |
| def dpstr(self, more=False): |
| ufid = self.get_attr("OVS_FLOW_ATTR_UFID") |
| ufid_str = "" |
| if ufid is not None: |
| ufid_str = ( |
| "ufid:{:08x}-{:04x}-{:04x}-{:04x}-{:04x}{:08x}".format( |
| ufid[0], |
| ufid[1] >> 16, |
| ufid[1] & 0xFFFF, |
| ufid[2] >> 16, |
| ufid[2] & 0, |
| ufid[3], |
| ) |
| ) |
| |
| key_field = self.get_attr("OVS_FLOW_ATTR_KEY") |
| keymsg = None |
| if key_field is not None: |
| keymsg = key_field |
| |
| mask_field = self.get_attr("OVS_FLOW_ATTR_MASK") |
| maskmsg = None |
| if mask_field is not None: |
| maskmsg = mask_field |
| |
| acts_field = self.get_attr("OVS_FLOW_ATTR_ACTIONS") |
| actsmsg = None |
| if acts_field is not None: |
| actsmsg = acts_field |
| |
| print_str = "" |
| |
| if more: |
| print_str += ufid_str + "," |
| |
| if keymsg is not None: |
| print_str += keymsg.dpstr(maskmsg, more) |
| |
| stats = self.get_attr("OVS_FLOW_ATTR_STATS") |
| if stats is None: |
| print_str += " packets:0, bytes:0," |
| else: |
| print_str += " packets:%d, bytes:%d," % ( |
| stats["packets"], |
| stats["bytes"], |
| ) |
| |
| used = self.get_attr("OVS_FLOW_ATTR_USED") |
| print_str += " used:" |
| if used is None: |
| print_str += "never," |
| else: |
| used_time = int(used) |
| cur_time_sec = time.clock_gettime(time.CLOCK_MONOTONIC) |
| used_time = (cur_time_sec * 1000) - used_time |
| print_str += "{}s,".format(used_time / 1000) |
| |
| print_str += " actions:" |
| if ( |
| actsmsg is None |
| or "attrs" not in actsmsg |
| or len(actsmsg["attrs"]) == 0 |
| ): |
| print_str += "drop" |
| else: |
| print_str += actsmsg.dpstr(more) |
| |
| return print_str |
| |
| def parse(self, flowstr, actstr, dpidx=0): |
| OVS_UFID_F_OMIT_KEY = 1 << 0 |
| OVS_UFID_F_OMIT_MASK = 1 << 1 |
| OVS_UFID_F_OMIT_ACTIONS = 1 << 2 |
| |
| self["cmd"] = 0 |
| self["version"] = 0 |
| self["reserved"] = 0 |
| self["dpifindex"] = 0 |
| |
| if flowstr.startswith("ufid:"): |
| count = 5 |
| while flowstr[count] != ",": |
| count += 1 |
| ufidstr = flowstr[5:count] |
| flowstr = flowstr[count + 1 :] |
| else: |
| ufidstr = str(uuid.uuid4()) |
| uuidRawObj = uuid.UUID(ufidstr).fields |
| |
| self["attrs"].append( |
| [ |
| "OVS_FLOW_ATTR_UFID", |
| [ |
| uuidRawObj[0], |
| uuidRawObj[1] << 16 | uuidRawObj[2], |
| uuidRawObj[3] << 24 |
| | uuidRawObj[4] << 16 |
| | uuidRawObj[5] & (0xFF << 32) >> 32, |
| uuidRawObj[5] & (0xFFFFFFFF), |
| ], |
| ] |
| ) |
| self["attrs"].append( |
| [ |
| "OVS_FLOW_ATTR_UFID_FLAGS", |
| int( |
| OVS_UFID_F_OMIT_KEY |
| | OVS_UFID_F_OMIT_MASK |
| | OVS_UFID_F_OMIT_ACTIONS |
| ), |
| ] |
| ) |
| |
| k = ovskey() |
| m = ovskey() |
| k.parse(flowstr, m) |
| self["attrs"].append(["OVS_FLOW_ATTR_KEY", k]) |
| self["attrs"].append(["OVS_FLOW_ATTR_MASK", m]) |
| |
| a = ovsactions() |
| a.parse(actstr) |
| self["attrs"].append(["OVS_FLOW_ATTR_ACTIONS", a]) |
| |
| def __init__(self): |
| GenericNetlinkSocket.__init__(self) |
| |
| self.bind(OVS_FLOW_FAMILY, OvsFlow.ovs_flow_msg) |
| |
| def add_flow(self, dpifindex, flowmsg): |
| """ |
| Send a new flow message to the kernel. |
| |
| dpifindex should be a valid datapath obtained by calling |
| into the OvsDatapath lookup |
| |
| flowmsg is a flow object obtained by calling a dpparse |
| """ |
| |
| flowmsg["cmd"] = OVS_FLOW_CMD_NEW |
| flowmsg["version"] = OVS_DATAPATH_VERSION |
| flowmsg["reserved"] = 0 |
| flowmsg["dpifindex"] = dpifindex |
| |
| try: |
| reply = self.nlm_request( |
| flowmsg, |
| msg_type=self.prid, |
| msg_flags=NLM_F_REQUEST | NLM_F_ACK, |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| print(flowmsg) |
| raise ne |
| return reply |
| |
| def del_flows(self, dpifindex): |
| """ |
| Send a del message to the kernel that will drop all flows. |
| |
| dpifindex should be a valid datapath obtained by calling |
| into the OvsDatapath lookup |
| """ |
| |
| flowmsg = OvsFlow.ovs_flow_msg() |
| flowmsg["cmd"] = OVS_FLOW_CMD_DEL |
| flowmsg["version"] = OVS_DATAPATH_VERSION |
| flowmsg["reserved"] = 0 |
| flowmsg["dpifindex"] = dpifindex |
| |
| try: |
| reply = self.nlm_request( |
| flowmsg, |
| msg_type=self.prid, |
| msg_flags=NLM_F_REQUEST | NLM_F_ACK, |
| ) |
| reply = reply[0] |
| except NetlinkError as ne: |
| print(flowmsg) |
| raise ne |
| return reply |
| |
| def dump(self, dpifindex, flowspec=None): |
| """ |
| Returns a list of messages containing flows. |
| |
| dpifindex should be a valid datapath obtained by calling |
| into the OvsDatapath lookup |
| |
| flowpsec is a string which represents a flow in the dpctl |
| format. |
| """ |
| msg = OvsFlow.ovs_flow_msg() |
| |
| msg["cmd"] = OVS_FLOW_CMD_GET |
| msg["version"] = OVS_DATAPATH_VERSION |
| msg["reserved"] = 0 |
| msg["dpifindex"] = dpifindex |
| |
| msg_flags = NLM_F_REQUEST | NLM_F_ACK |
| if flowspec is None: |
| msg_flags |= NLM_F_DUMP |
| rep = None |
| |
| try: |
| rep = self.nlm_request( |
| msg, |
| msg_type=self.prid, |
| msg_flags=msg_flags, |
| ) |
| except NetlinkError as ne: |
| raise ne |
| return rep |
| |
| def miss(self, packetmsg): |
| seq = packetmsg["header"]["sequence_number"] |
| keystr = "(none)" |
| key_field = packetmsg.get_attr("OVS_PACKET_ATTR_KEY") |
| if key_field is not None: |
| keystr = key_field.dpstr(None, True) |
| |
| pktdata = packetmsg.get_attr("OVS_PACKET_ATTR_PACKET") |
| pktpres = "yes" if pktdata is not None else "no" |
| |
| print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True) |
| |
| def execute(self, packetmsg): |
| print("userspace execute command", flush=True) |
| |
| def action(self, packetmsg): |
| print("userspace action command", flush=True) |
| |
| |
| class psample_sample(genlmsg): |
| nla_map = ( |
| ("PSAMPLE_ATTR_IIFINDEX", "none"), |
| ("PSAMPLE_ATTR_OIFINDEX", "none"), |
| ("PSAMPLE_ATTR_ORIGSIZE", "none"), |
| ("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"), |
| ("PSAMPLE_ATTR_GROUP_SEQ", "none"), |
| ("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"), |
| ("PSAMPLE_ATTR_DATA", "array(uint8)"), |
| ("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"), |
| ("PSAMPLE_ATTR_TUNNEL", "none"), |
| ("PSAMPLE_ATTR_PAD", "none"), |
| ("PSAMPLE_ATTR_OUT_TC", "none"), |
| ("PSAMPLE_ATTR_OUT_TC_OCC", "none"), |
| ("PSAMPLE_ATTR_LATENCY", "none"), |
| ("PSAMPLE_ATTR_TIMESTAMP", "none"), |
| ("PSAMPLE_ATTR_PROTO", "none"), |
| ("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"), |
| ) |
| |
| def dpstr(self): |
| fields = [] |
| data = "" |
| for (attr, value) in self["attrs"]: |
| if attr == "PSAMPLE_ATTR_SAMPLE_GROUP": |
| fields.append("group:%d" % value) |
| if attr == "PSAMPLE_ATTR_SAMPLE_RATE": |
| fields.append("rate:%d" % value) |
| if attr == "PSAMPLE_ATTR_USER_COOKIE": |
| value = "".join(format(x, "02x") for x in value) |
| fields.append("cookie:%s" % value) |
| if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0: |
| data = "data:%s" % "".join(format(x, "02x") for x in value) |
| |
| return ("%s %s" % (",".join(fields), data)).strip() |
| |
| |
| class psample_msg(Marshal): |
| PSAMPLE_CMD_SAMPLE = 0 |
| PSAMPLE_CMD_GET_GROUP = 1 |
| PSAMPLE_CMD_NEW_GROUP = 2 |
| PSAMPLE_CMD_DEL_GROUP = 3 |
| PSAMPLE_CMD_SET_FILTER = 4 |
| msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample} |
| |
| |
| class PsampleEvent(EventSocket): |
| genl_family = "psample" |
| mcast_groups = ["packets"] |
| marshal_class = psample_msg |
| |
| def read_samples(self): |
| print("listening for psample events", flush=True) |
| while True: |
| try: |
| for msg in self.get(): |
| print(msg.dpstr(), flush=True) |
| except NetlinkError as ne: |
| raise ne |
| |
| |
| def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): |
| dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME") |
| base_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_STATS") |
| megaflow_stats = dp_lookup_rep.get_attr("OVS_DP_ATTR_MEGAFLOW_STATS") |
| user_features = dp_lookup_rep.get_attr("OVS_DP_ATTR_USER_FEATURES") |
| masks_cache_size = dp_lookup_rep.get_attr("OVS_DP_ATTR_MASKS_CACHE_SIZE") |
| |
| print("%s:" % dp_name) |
| print( |
| " lookups: hit:%d missed:%d lost:%d" |
| % (base_stats["hit"], base_stats["missed"], base_stats["lost"]) |
| ) |
| print(" flows:%d" % base_stats["flows"]) |
| pkts = base_stats["hit"] + base_stats["missed"] |
| avg = (megaflow_stats["mask_hit"] / pkts) if pkts != 0 else 0.0 |
| print( |
| " masks: hit:%d total:%d hit/pkt:%f" |
| % (megaflow_stats["mask_hit"], megaflow_stats["masks"], avg) |
| ) |
| print(" caches:") |
| print(" masks-cache: size:%d" % masks_cache_size) |
| |
| if user_features is not None: |
| print(" features: 0x%X" % user_features) |
| |
| # port print out |
| for iface in ndb.interfaces: |
| rep = vpl.info(iface.ifname, ifindex) |
| if rep is not None: |
| opts = "" |
| vpo = rep.get_attr("OVS_VPORT_ATTR_OPTIONS") |
| if vpo: |
| dpo = vpo.get_attr("OVS_TUNNEL_ATTR_DST_PORT") |
| if dpo: |
| opts += " tnl-dport:%s" % socket.ntohs(dpo) |
| print( |
| " port %d: %s (%s%s)" |
| % ( |
| rep.get_attr("OVS_VPORT_ATTR_PORT_NO"), |
| rep.get_attr("OVS_VPORT_ATTR_NAME"), |
| OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")), |
| opts, |
| ) |
| ) |
| |
| |
| def main(argv): |
| nlmsg_atoms.ovskey = ovskey |
| nlmsg_atoms.ovsactions = ovsactions |
| |
| # version check for pyroute2 |
| prverscheck = pyroute2.__version__.split(".") |
| if int(prverscheck[0]) == 0 and int(prverscheck[1]) < 6: |
| print("Need to upgrade the python pyroute2 package to >= 0.6.") |
| sys.exit(0) |
| |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| "-v", |
| "--verbose", |
| action="count", |
| help="Increment 'verbose' output counter.", |
| default=0, |
| ) |
| subparsers = parser.add_subparsers(dest="subcommand") |
| |
| showdpcmd = subparsers.add_parser("show") |
| showdpcmd.add_argument( |
| "showdp", metavar="N", type=str, nargs="?", help="Datapath Name" |
| ) |
| |
| adddpcmd = subparsers.add_parser("add-dp") |
| adddpcmd.add_argument("adddp", help="Datapath Name") |
| adddpcmd.add_argument( |
| "-u", |
| "--upcall", |
| action="store_true", |
| help="Leave open a reader for upcalls", |
| ) |
| adddpcmd.add_argument( |
| "-V", |
| "--versioning", |
| required=False, |
| help="Specify a custom version / feature string", |
| ) |
| |
| deldpcmd = subparsers.add_parser("del-dp") |
| deldpcmd.add_argument("deldp", help="Datapath Name") |
| |
| addifcmd = subparsers.add_parser("add-if") |
| addifcmd.add_argument("dpname", help="Datapath Name") |
| addifcmd.add_argument("addif", help="Interface name for adding") |
| addifcmd.add_argument( |
| "-u", |
| "--upcall", |
| action="store_true", |
| help="Leave open a reader for upcalls", |
| ) |
| addifcmd.add_argument( |
| "-t", |
| "--ptype", |
| type=str, |
| default="netdev", |
| choices=["netdev", "internal", "geneve", "vxlan"], |
| help="Interface type (default netdev)", |
| ) |
| addifcmd.add_argument( |
| "-p", |
| "--dport", |
| type=int, |
| default=0, |
| help="Destination port (0 for default)" |
| ) |
| addifcmd.add_argument( |
| "-l", |
| "--lwt", |
| type=bool, |
| default=True, |
| help="Use LWT infrastructure instead of vport (default true)." |
| ) |
| delifcmd = subparsers.add_parser("del-if") |
| delifcmd.add_argument("dpname", help="Datapath Name") |
| delifcmd.add_argument("delif", help="Interface name for adding") |
| delifcmd.add_argument("-d", |
| "--dellink", |
| type=bool, default=False, |
| help="Delete the link as well.") |
| |
| dumpflcmd = subparsers.add_parser("dump-flows") |
| dumpflcmd.add_argument("dumpdp", help="Datapath Name") |
| |
| addflcmd = subparsers.add_parser("add-flow") |
| addflcmd.add_argument("flbr", help="Datapath name") |
| addflcmd.add_argument("flow", help="Flow specification") |
| addflcmd.add_argument("acts", help="Flow actions") |
| |
| delfscmd = subparsers.add_parser("del-flows") |
| delfscmd.add_argument("flsbr", help="Datapath name") |
| |
| subparsers.add_parser("psample-events") |
| |
| args = parser.parse_args() |
| |
| if args.verbose > 0: |
| if args.verbose > 1: |
| logging.basicConfig(level=logging.DEBUG) |
| |
| ovspk = OvsPacket() |
| ovsdp = OvsDatapath() |
| ovsvp = OvsVport(ovspk) |
| ovsflow = OvsFlow() |
| ndb = NDB() |
| |
| sys.setrecursionlimit(100000) |
| |
| if args.subcommand == "psample-events": |
| PsampleEvent().read_samples() |
| |
| if hasattr(args, "showdp"): |
| found = False |
| for iface in ndb.interfaces: |
| rep = None |
| if args.showdp is None: |
| rep = ovsdp.info(iface.ifname, 0) |
| elif args.showdp == iface.ifname: |
| rep = ovsdp.info(iface.ifname, 0) |
| |
| if rep is not None: |
| found = True |
| print_ovsdp_full(rep, iface.index, ndb, ovsvp) |
| |
| if not found: |
| msg = "No DP found" |
| if args.showdp is not None: |
| msg += ":'%s'" % args.showdp |
| print(msg) |
| elif hasattr(args, "adddp"): |
| rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk) |
| if rep is None: |
| print("DP '%s' already exists" % args.adddp) |
| else: |
| print("DP '%s' added" % args.adddp) |
| if args.upcall: |
| ovspk.upcall_handler(ovsflow) |
| elif hasattr(args, "deldp"): |
| ovsdp.destroy(args.deldp) |
| elif hasattr(args, "addif"): |
| rep = ovsdp.info(args.dpname, 0) |
| if rep is None: |
| print("DP '%s' not found." % args.dpname) |
| return 1 |
| dpindex = rep["dpifindex"] |
| rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype, |
| args.dport, args.lwt) |
| msg = "vport '%s'" % args.addif |
| if rep and rep["header"]["error"] is None: |
| msg += " added." |
| else: |
| msg += " failed to add." |
| if args.upcall: |
| if rep is None: |
| rep = ovsvp.reset_upcall(dpindex, args.addif, ovspk) |
| ovsvp.upcall_handler(ovsflow) |
| elif hasattr(args, "delif"): |
| rep = ovsdp.info(args.dpname, 0) |
| if rep is None: |
| print("DP '%s' not found." % args.dpname) |
| return 1 |
| rep = ovsvp.detach(rep["dpifindex"], args.delif) |
| msg = "vport '%s'" % args.delif |
| if rep and rep["header"]["error"] is None: |
| msg += " removed." |
| else: |
| msg += " failed to remove." |
| if args.dellink: |
| ipr = pyroute2.iproute.IPRoute() |
| ipr.link("del", index=ipr.link_lookup(ifname=args.delif)[0]) |
| elif hasattr(args, "dumpdp"): |
| rep = ovsdp.info(args.dumpdp, 0) |
| if rep is None: |
| print("DP '%s' not found." % args.dumpdp) |
| return 1 |
| rep = ovsflow.dump(rep["dpifindex"]) |
| for flow in rep: |
| print(flow.dpstr(True if args.verbose > 0 else False)) |
| elif hasattr(args, "flbr"): |
| rep = ovsdp.info(args.flbr, 0) |
| if rep is None: |
| print("DP '%s' not found." % args.flbr) |
| return 1 |
| flow = OvsFlow.ovs_flow_msg() |
| flow.parse(args.flow, args.acts, rep["dpifindex"]) |
| ovsflow.add_flow(rep["dpifindex"], flow) |
| elif hasattr(args, "flsbr"): |
| rep = ovsdp.info(args.flsbr, 0) |
| if rep is None: |
| print("DP '%s' not found." % args.flsbr) |
| ovsflow.del_flows(rep["dpifindex"]) |
| |
| return 0 |
| |
| |
| if __name__ == "__main__": |
| sys.exit(main(sys.argv)) |