The openvswitch self-tests can test much of the control side of the module (ie: what a vswitchd implementation would process), but the actual packet forwarding cases aren't supported, making the testing of limited value. Add some flow parsing and an initial ARP based test case using arping utility. This lets us display flows, add some basic output flows with simple matches, and test against a known good forwarding case. Signed-off-by: Aaron Conole <aconole@redhat.com> Reviewed-by: Adrian Moreno <amorenoz@redhat.com> Reviewed-by: Simon Horman <horms@kernel.org> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
2005 lines
61 KiB
Python
2005 lines
61 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 multiprocessing
|
|
import re
|
|
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.exceptions import NetlinkError
|
|
from pyroute2.netlink.generic import GenericNetlinkSocket
|
|
except ModuleNotFoundError:
|
|
print("Need to install the python pyroute2 package.")
|
|
sys.exit(0)
|
|
|
|
|
|
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
|
|
|
|
|
|
def macstr(mac):
|
|
outstr = ":".join(["%02X" % i for i in mac])
|
|
return outstr
|
|
|
|
|
|
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(mac_str, mask=False):
|
|
if mac_str is None or mac_str == "":
|
|
mac_str = "00:00:00:00:00:00"
|
|
if mask is True and mac_str != "00:00:00:00:00:00":
|
|
mac_str = "FF:FF:FF:FF:FF:FF"
|
|
mac_split = mac_str.split(":")
|
|
ret = bytearray([int(i, 16) for i in mac_split])
|
|
return bytes(ret)
|
|
|
|
|
|
def convert_ipv4(ip, mask=False):
|
|
if ip is None:
|
|
ip = 0
|
|
if mask is True:
|
|
if ip != 0:
|
|
ip = int(ipaddress.IPv4Address(ip)) & 0xFFFFFFFF
|
|
|
|
return int(ipaddress.IPv4Address(ip))
|
|
|
|
|
|
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
|
|
|
|
|
|
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", "none"),
|
|
("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
|
|
("OVS_ACTION_ATTR_POP_VLAN", "flag"),
|
|
("OVS_ACTION_ATTR_SAMPLE", "none"),
|
|
("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", "none"),
|
|
("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", "none"),
|
|
("OVS_ACTION_ATTR_CHECK_PKT_LEN", "none"),
|
|
("OVS_ACTION_ATTR_ADD_MPLS", "none"),
|
|
("OVS_ACTION_ATTR_DEC_TTL", "none"),
|
|
)
|
|
|
|
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_TUN_PORT") is not None:
|
|
print_str += "egress_tun_port=%d" % self.get_attr(
|
|
"OVS_USERSPACE_ATTR_TUN_PORT"
|
|
)
|
|
print_str += ")"
|
|
return print_str
|
|
|
|
def dpstr(self, more=False):
|
|
print_str = ""
|
|
|
|
for field in self.nla_map:
|
|
if field[1] == "none" or self.get_attr(field[0]) is None:
|
|
continue
|
|
if print_str != "":
|
|
print_str += ","
|
|
|
|
if field[1] == "uint32":
|
|
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[1] == "flag":
|
|
if 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])
|
|
print_str += datum.dpstr(more)
|
|
|
|
return print_str
|
|
|
|
def parse(self, actstr):
|
|
while len(actstr) != 0:
|
|
parsed = False
|
|
if actstr.startswith("drop"):
|
|
# for now, drops have no explicit action, so we
|
|
# don't need to set any attributes. The final
|
|
# act of the processing chain will just drop the packet
|
|
return
|
|
|
|
elif parse_starts_block(actstr, "^(\d+)", False, True):
|
|
actstr, output = parse_extract_field(
|
|
actstr, None, "(\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(",
|
|
"([0-9a-fA-Fx]+)",
|
|
lambda x: int(x, 0),
|
|
False,
|
|
0,
|
|
)
|
|
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 += len(flat_act[0])
|
|
self["attrs"].append([flat_act[1]])
|
|
actstr = actstr[strspn(actstr, ", ") :]
|
|
parsed = True
|
|
|
|
if not parsed:
|
|
raise ValueError("Action str: '%s' not supported" % 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", "none"),
|
|
("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 is not None else 0),
|
|
("dst", "dst", "%d", lambda x: int(x) if x is not None else 0),
|
|
)
|
|
|
|
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 = None
|
|
|
|
if len(f) > 4:
|
|
func = f[4]
|
|
else:
|
|
func = f[3]
|
|
k[f[0]] = func(data)
|
|
if len(f) > 4:
|
|
m[f[0]] = func(data, True)
|
|
else:
|
|
m[f[0]] = func(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 is not None else 0),
|
|
("tos", "tos", "%d", lambda x: int(x) if x is not None else 0),
|
|
("ttl", "ttl", "%d", lambda x: int(x) if x is not None else 0),
|
|
("frag", "frag", "%d", lambda x: int(x) if x is not None else 0),
|
|
)
|
|
|
|
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: int.from_bytes(x, "big"),
|
|
lambda x: ipaddress.IPv6Address(x),
|
|
),
|
|
(
|
|
"dst",
|
|
"dst",
|
|
lambda x: str(ipaddress.IPv6Address(x)),
|
|
lambda x: int.from_bytes(x, "big"),
|
|
lambda x: ipaddress.IPv6Address(x),
|
|
),
|
|
("label", "label", "%d", int),
|
|
("proto", "proto", "%d", int),
|
|
("tclass", "tclass", "%d", int),
|
|
("hlimit", "hlimit", "%d", int),
|
|
("frag", "frag", "%d", int),
|
|
)
|
|
|
|
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", int),
|
|
("code", "code", "%d", int),
|
|
)
|
|
|
|
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 is not None 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)),
|
|
lambda x: int.from_bytes(x, "big"),
|
|
),
|
|
("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,
|
|
),
|
|
(
|
|
"dst",
|
|
"dst",
|
|
lambda x: str(ipaddress.IPv6Address(x)),
|
|
int,
|
|
),
|
|
("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)),
|
|
lambda x: int.from_bytes(x, "big", convertmac),
|
|
),
|
|
(
|
|
"dst",
|
|
"dst",
|
|
lambda x: str(ipaddress.IPv6Address(x)),
|
|
lambda x: int.from_bytes(x, "big"),
|
|
),
|
|
("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_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_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_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_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", "none"),
|
|
("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 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):
|
|
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_TYPE", port_type])
|
|
msg["attrs"].append(["OVS_VPORT_ATTR_NAME", vport_ifname])
|
|
msg["attrs"].append(
|
|
["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.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:
|
|
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 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")
|
|
|
|
def action(self, packetmsg):
|
|
print("userspace action command")
|
|
|
|
|
|
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:
|
|
print(
|
|
" port %d: %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")),
|
|
)
|
|
)
|
|
|
|
|
|
def main(argv):
|
|
nlmsg_atoms.ovskey = ovskey
|
|
nlmsg_atoms.ovsactions = ovsactions
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"-v",
|
|
"--verbose",
|
|
action="count",
|
|
help="Increment 'verbose' output counter.",
|
|
default=0,
|
|
)
|
|
subparsers = parser.add_subparsers()
|
|
|
|
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"],
|
|
help="Interface type (default netdev)",
|
|
)
|
|
delifcmd = subparsers.add_parser("del-if")
|
|
delifcmd.add_argument("dpname", help="Datapath Name")
|
|
delifcmd.add_argument("delif", help="Interface name for adding")
|
|
|
|
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")
|
|
|
|
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()
|
|
|
|
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)
|
|
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."
|
|
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)
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|