2776 lines
89 KiB
Python
2776 lines
89 KiB
Python
|
#!/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))
|