Commit 3a158e2e authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'selftests-net-switch-pmtu-sh-to-use-the-internal-ovs-script'

Aaron Conole says:

====================
selftests: net: Switch pmtu.sh to use the internal ovs script.

Currently, if a user wants to run pmtu.sh and cover all the provided test
cases, they need to install the Open vSwitch userspace utilities.  This
dependency is difficult for users as well as CI environments, because the
userspace build and setup may require lots of support and devel packages
to be installed, system setup to be correct, and things like permissions
and selinux policies to be properly configured.

The kernel selftest suite includes an ovs-dpctl.py utility which can
interact with the openvswitch module directly.  This lets developers and
CI environments run without needing too many extra dependencies - just
the pyroute2 python package.

This series enhances the ovs-dpctl utility to provide support for set()
and tunnel() flow specifiers, better ipv6 handling support, and the
ability to add tunnel vports, and LWT interfaces.  Finally, it modifies
the pmtu.sh script to call the ovs-dpctl.py utility rather than the
typical OVS userspace utilities.  The pmtu.sh can still fall back on
the Open vSwitch userspace utilities if the ovs-dpctl.py script can't
be used.
====================

Link: https://patch.msgid.link/20240625172245.233874-1-aconole@redhat.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 346a03e5 6f437f5c
...@@ -74,7 +74,12 @@ CONFIG_NET_SCH_ETF=m ...@@ -74,7 +74,12 @@ CONFIG_NET_SCH_ETF=m
CONFIG_NET_SCH_NETEM=y CONFIG_NET_SCH_NETEM=y
CONFIG_NET_SCH_PRIO=m CONFIG_NET_SCH_PRIO=m
CONFIG_NFT_COMPAT=m CONFIG_NFT_COMPAT=m
CONFIG_NF_CONNTRACK_OVS=y
CONFIG_NF_FLOW_TABLE=m CONFIG_NF_FLOW_TABLE=m
CONFIG_OPENVSWITCH=m
CONFIG_OPENVSWITCH_GENEVE=m
CONFIG_OPENVSWITCH_GRE=m
CONFIG_OPENVSWITCH_VXLAN=m
CONFIG_PSAMPLE=m CONFIG_PSAMPLE=m
CONFIG_TCP_MD5SIG=y CONFIG_TCP_MD5SIG=y
CONFIG_TEST_BLACKHOLE_DEV=m CONFIG_TEST_BLACKHOLE_DEV=m
......
...@@ -10,6 +10,7 @@ import ipaddress ...@@ -10,6 +10,7 @@ import ipaddress
import logging import logging
import multiprocessing import multiprocessing
import re import re
import socket
import struct import struct
import sys import sys
import time import time
...@@ -29,10 +30,11 @@ try: ...@@ -29,10 +30,11 @@ try:
from pyroute2.netlink.exceptions import NetlinkError from pyroute2.netlink.exceptions import NetlinkError
from pyroute2.netlink.generic import GenericNetlinkSocket from pyroute2.netlink.generic import GenericNetlinkSocket
import pyroute2 import pyroute2
import pyroute2.iproute
except ModuleNotFoundError: except ModuleNotFoundError:
print("Need to install the python pyroute2 package >= 0.6.") print("Need to install the python pyroute2 package >= 0.6.")
sys.exit(0) sys.exit(1)
OVS_DATAPATH_FAMILY = "ovs_datapath" OVS_DATAPATH_FAMILY = "ovs_datapath"
...@@ -198,6 +200,18 @@ def convert_ipv4(data): ...@@ -198,6 +200,18 @@ def convert_ipv4(data):
return int(ipaddress.IPv4Address(ip)), int(ipaddress.IPv4Address(mask)) 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(size):
def convert_int_sized(data): def convert_int_sized(data):
value, _, mask = data.partition('/') value, _, mask = data.partition('/')
...@@ -282,7 +296,7 @@ class ovsactions(nla): ...@@ -282,7 +296,7 @@ class ovsactions(nla):
("OVS_ACTION_ATTR_UNSPEC", "none"), ("OVS_ACTION_ATTR_UNSPEC", "none"),
("OVS_ACTION_ATTR_OUTPUT", "uint32"), ("OVS_ACTION_ATTR_OUTPUT", "uint32"),
("OVS_ACTION_ATTR_USERSPACE", "userspace"), ("OVS_ACTION_ATTR_USERSPACE", "userspace"),
("OVS_ACTION_ATTR_SET", "none"), ("OVS_ACTION_ATTR_SET", "ovskey"),
("OVS_ACTION_ATTR_PUSH_VLAN", "none"), ("OVS_ACTION_ATTR_PUSH_VLAN", "none"),
("OVS_ACTION_ATTR_POP_VLAN", "flag"), ("OVS_ACTION_ATTR_POP_VLAN", "flag"),
("OVS_ACTION_ATTR_SAMPLE", "none"), ("OVS_ACTION_ATTR_SAMPLE", "none"),
...@@ -290,7 +304,7 @@ class ovsactions(nla): ...@@ -290,7 +304,7 @@ class ovsactions(nla):
("OVS_ACTION_ATTR_HASH", "none"), ("OVS_ACTION_ATTR_HASH", "none"),
("OVS_ACTION_ATTR_PUSH_MPLS", "none"), ("OVS_ACTION_ATTR_PUSH_MPLS", "none"),
("OVS_ACTION_ATTR_POP_MPLS", "flag"), ("OVS_ACTION_ATTR_POP_MPLS", "flag"),
("OVS_ACTION_ATTR_SET_MASKED", "none"), ("OVS_ACTION_ATTR_SET_MASKED", "ovskey"),
("OVS_ACTION_ATTR_CT", "ctact"), ("OVS_ACTION_ATTR_CT", "ctact"),
("OVS_ACTION_ATTR_TRUNC", "uint32"), ("OVS_ACTION_ATTR_TRUNC", "uint32"),
("OVS_ACTION_ATTR_PUSH_ETH", "none"), ("OVS_ACTION_ATTR_PUSH_ETH", "none"),
...@@ -437,40 +451,53 @@ class ovsactions(nla): ...@@ -437,40 +451,53 @@ class ovsactions(nla):
def dpstr(self, more=False): def dpstr(self, more=False):
print_str = "" print_str = ""
for field in self.nla_map: for field in self["attrs"]:
if field[1] == "none" or self.get_attr(field[0]) is None: if field[1] == "none" or self.get_attr(field[0]) is None:
continue continue
if print_str != "": if print_str != "":
print_str += "," print_str += ","
if field[1] == "uint32": if field[0] == "OVS_ACTION_ATTR_OUTPUT":
if field[0] == "OVS_ACTION_ATTR_OUTPUT": print_str += "%d" % int(self.get_attr(field[0]))
print_str += "%d" % int(self.get_attr(field[0])) elif field[0] == "OVS_ACTION_ATTR_RECIRC":
elif field[0] == "OVS_ACTION_ATTR_RECIRC": print_str += "recirc(0x%x)" % int(self.get_attr(field[0]))
print_str += "recirc(0x%x)" % int(self.get_attr(field[0])) elif field[0] == "OVS_ACTION_ATTR_TRUNC":
elif field[0] == "OVS_ACTION_ATTR_TRUNC": print_str += "trunc(%d)" % int(self.get_attr(field[0]))
print_str += "trunc(%d)" % int(self.get_attr(field[0])) elif field[0] == "OVS_ACTION_ATTR_DROP":
elif field[0] == "OVS_ACTION_ATTR_DROP": print_str += "drop(%d)" % int(self.get_attr(field[0]))
print_str += "drop(%d)" % int(self.get_attr(field[0])) elif field[0] == "OVS_ACTION_ATTR_CT_CLEAR":
elif field[1] == "flag": print_str += "ct_clear"
if field[0] == "OVS_ACTION_ATTR_CT_CLEAR": elif field[0] == "OVS_ACTION_ATTR_POP_VLAN":
print_str += "ct_clear" print_str += "pop_vlan"
elif field[0] == "OVS_ACTION_ATTR_POP_VLAN": elif field[0] == "OVS_ACTION_ATTR_POP_ETH":
print_str += "pop_vlan" print_str += "pop_eth"
elif field[0] == "OVS_ACTION_ATTR_POP_ETH": elif field[0] == "OVS_ACTION_ATTR_POP_NSH":
print_str += "pop_eth" print_str += "pop_nsh"
elif field[0] == "OVS_ACTION_ATTR_POP_NSH": elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":
print_str += "pop_nsh" print_str += "pop_mpls"
elif field[0] == "OVS_ACTION_ATTR_POP_MPLS":
print_str += "pop_mpls"
else: else:
datum = self.get_attr(field[0]) datum = self.get_attr(field[0])
if field[0] == "OVS_ACTION_ATTR_CLONE": if field[0] == "OVS_ACTION_ATTR_CLONE":
print_str += "clone(" print_str += "clone("
print_str += datum.dpstr(more) print_str += datum.dpstr(more)
print_str += ")" 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: else:
print_str += datum.dpstr(more) try:
print_str += datum.dpstr(more)
except:
print_str += "{ATTR: %s not decoded}" % field[0]
return print_str return print_str
...@@ -544,6 +571,25 @@ class ovsactions(nla): ...@@ -544,6 +571,25 @@ class ovsactions(nla):
self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts)) self["attrs"].append(("OVS_ACTION_ATTR_CLONE", subacts))
actstr = actstr[parsedLen:] actstr = actstr[parsedLen:]
parsed = True 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): elif parse_starts_block(actstr, "ct(", False):
parencount += 1 parencount += 1
actstr = actstr[len("ct(") :] actstr = actstr[len("ct(") :]
...@@ -675,7 +721,7 @@ class ovskey(nla): ...@@ -675,7 +721,7 @@ class ovskey(nla):
("OVS_KEY_ATTR_ARP", "ovs_key_arp"), ("OVS_KEY_ATTR_ARP", "ovs_key_arp"),
("OVS_KEY_ATTR_ND", "ovs_key_nd"), ("OVS_KEY_ATTR_ND", "ovs_key_nd"),
("OVS_KEY_ATTR_SKB_MARK", "uint32"), ("OVS_KEY_ATTR_SKB_MARK", "uint32"),
("OVS_KEY_ATTR_TUNNEL", "none"), ("OVS_KEY_ATTR_TUNNEL", "ovs_key_tunnel"),
("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"), ("OVS_KEY_ATTR_SCTP", "ovs_key_sctp"),
("OVS_KEY_ATTR_TCP_FLAGS", "be16"), ("OVS_KEY_ATTR_TCP_FLAGS", "be16"),
("OVS_KEY_ATTR_DP_HASH", "uint32"), ("OVS_KEY_ATTR_DP_HASH", "uint32"),
...@@ -907,21 +953,21 @@ class ovskey(nla): ...@@ -907,21 +953,21 @@ class ovskey(nla):
"src", "src",
"src", "src",
lambda x: str(ipaddress.IPv6Address(x)), lambda x: str(ipaddress.IPv6Address(x)),
lambda x: int.from_bytes(x, "big"), lambda x: ipaddress.IPv6Address(x).packed if x else 0,
lambda x: ipaddress.IPv6Address(x), convert_ipv6,
), ),
( (
"dst", "dst",
"dst", "dst",
lambda x: str(ipaddress.IPv6Address(x)), lambda x: str(ipaddress.IPv6Address(x)),
lambda x: int.from_bytes(x, "big"), lambda x: ipaddress.IPv6Address(x).packed if x else 0,
lambda x: ipaddress.IPv6Address(x), convert_ipv6,
), ),
("label", "label", "%d", int), ("label", "label", "%d", lambda x: int(x) if x else 0),
("proto", "proto", "%d", int), ("proto", "proto", "%d", lambda x: int(x) if x else 0),
("tclass", "tclass", "%d", int), ("tclass", "tclass", "%d", lambda x: int(x) if x else 0),
("hlimit", "hlimit", "%d", int), ("hlimit", "hlimit", "%d", lambda x: int(x) if x else 0),
("frag", "frag", "%d", int), ("frag", "frag", "%d", lambda x: int(x) if x else 0),
) )
def __init__( def __init__(
...@@ -1119,7 +1165,7 @@ class ovskey(nla): ...@@ -1119,7 +1165,7 @@ class ovskey(nla):
"target", "target",
"target", "target",
lambda x: str(ipaddress.IPv6Address(x)), lambda x: str(ipaddress.IPv6Address(x)),
lambda x: int.from_bytes(x, "big"), convert_ipv6,
), ),
("sll", "sll", macstr, 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")), ("tll", "tll", macstr, lambda x: int.from_bytes(x, "big")),
...@@ -1204,13 +1250,13 @@ class ovskey(nla): ...@@ -1204,13 +1250,13 @@ class ovskey(nla):
"src", "src",
"src", "src",
lambda x: str(ipaddress.IPv6Address(x)), lambda x: str(ipaddress.IPv6Address(x)),
lambda x: int.from_bytes(x, "big", convertmac), convert_ipv6,
), ),
( (
"dst", "dst",
"dst", "dst",
lambda x: str(ipaddress.IPv6Address(x)), lambda x: str(ipaddress.IPv6Address(x)),
lambda x: int.from_bytes(x, "big"), convert_ipv6,
), ),
("tp_src", "tp_src", "%d", int), ("tp_src", "tp_src", "%d", int),
("tp_dst", "tp_dst", "%d", int), ("tp_dst", "tp_dst", "%d", int),
...@@ -1235,6 +1281,163 @@ class ovskey(nla): ...@@ -1235,6 +1281,163 @@ class ovskey(nla):
init=init, 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): class ovs_key_mpls(nla):
fields = (("lse", ">I"),) fields = (("lse", ">I"),)
...@@ -1243,6 +1446,7 @@ class ovskey(nla): ...@@ -1243,6 +1446,7 @@ class ovskey(nla):
("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse), ("OVS_KEY_ATTR_PRIORITY", "skb_priority", intparse),
("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse), ("OVS_KEY_ATTR_SKB_MARK", "skb_mark", intparse),
("OVS_KEY_ATTR_RECIRC_ID", "recirc_id", 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_DP_HASH", "dp_hash", intparse),
("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state), ("OVS_KEY_ATTR_CT_STATE", "ct_state", parse_ct_state),
("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse), ("OVS_KEY_ATTR_CT_ZONE", "ct_zone", intparse),
...@@ -1309,7 +1513,7 @@ class ovskey(nla): ...@@ -1309,7 +1513,7 @@ class ovskey(nla):
mask["attrs"].append([field[0], m]) mask["attrs"].append([field[0], m])
self["attrs"].append([field[0], k]) self["attrs"].append([field[0], k])
flowstr = flowstr[strspn(flowstr, "),") :] flowstr = flowstr[strspn(flowstr, "), ") :]
return flowstr return flowstr
...@@ -1345,6 +1549,13 @@ class ovskey(nla): ...@@ -1345,6 +1549,13 @@ class ovskey(nla):
lambda x: False, lambda x: False,
True, True,
), ),
(
"OVS_KEY_ATTR_TUNNEL",
"tunnel",
None,
False,
False,
),
( (
"OVS_KEY_ATTR_CT_STATE", "OVS_KEY_ATTR_CT_STATE",
"ct_state", "ct_state",
...@@ -1617,7 +1828,7 @@ class OvsVport(GenericNetlinkSocket): ...@@ -1617,7 +1828,7 @@ class OvsVport(GenericNetlinkSocket):
("OVS_VPORT_ATTR_PORT_NO", "uint32"), ("OVS_VPORT_ATTR_PORT_NO", "uint32"),
("OVS_VPORT_ATTR_TYPE", "uint32"), ("OVS_VPORT_ATTR_TYPE", "uint32"),
("OVS_VPORT_ATTR_NAME", "asciiz"), ("OVS_VPORT_ATTR_NAME", "asciiz"),
("OVS_VPORT_ATTR_OPTIONS", "none"), ("OVS_VPORT_ATTR_OPTIONS", "vportopts"),
("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"), ("OVS_VPORT_ATTR_UPCALL_PID", "array(uint32)"),
("OVS_VPORT_ATTR_STATS", "vportstats"), ("OVS_VPORT_ATTR_STATS", "vportstats"),
("OVS_VPORT_ATTR_PAD", "none"), ("OVS_VPORT_ATTR_PAD", "none"),
...@@ -1625,6 +1836,13 @@ class OvsVport(GenericNetlinkSocket): ...@@ -1625,6 +1836,13 @@ class OvsVport(GenericNetlinkSocket):
("OVS_VPORT_ATTR_NETNSID", "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): class vportstats(nla):
fields = ( fields = (
("rx_packets", "=Q"), ("rx_packets", "=Q"),
...@@ -1693,7 +1911,7 @@ class OvsVport(GenericNetlinkSocket): ...@@ -1693,7 +1911,7 @@ class OvsVport(GenericNetlinkSocket):
raise ne raise ne
return reply return reply
def attach(self, dpindex, vport_ifname, ptype): def attach(self, dpindex, vport_ifname, ptype, dport, lwt):
msg = OvsVport.ovs_vport_msg() msg = OvsVport.ovs_vport_msg()
msg["cmd"] = OVS_VPORT_CMD_NEW msg["cmd"] = OVS_VPORT_CMD_NEW
...@@ -1702,12 +1920,43 @@ class OvsVport(GenericNetlinkSocket): ...@@ -1702,12 +1920,43 @@ class OvsVport(GenericNetlinkSocket):
msg["dpifindex"] = dpindex msg["dpifindex"] = dpindex
port_type = OvsVport.str_to_type(ptype) 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_NAME", vport_ifname])
msg["attrs"].append( msg["attrs"].append(
["OVS_VPORT_ATTR_UPCALL_PID", [self.upcall_packet.epid]] ["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: try:
reply = self.nlm_request( reply = self.nlm_request(
msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK msg, msg_type=self.prid, msg_flags=NLM_F_REQUEST | NLM_F_ACK
...@@ -2053,12 +2302,19 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()): ...@@ -2053,12 +2302,19 @@ def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
for iface in ndb.interfaces: for iface in ndb.interfaces:
rep = vpl.info(iface.ifname, ifindex) rep = vpl.info(iface.ifname, ifindex)
if rep is not None: 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( print(
" port %d: %s (%s)" " port %d: %s (%s%s)"
% ( % (
rep.get_attr("OVS_VPORT_ATTR_PORT_NO"), rep.get_attr("OVS_VPORT_ATTR_PORT_NO"),
rep.get_attr("OVS_VPORT_ATTR_NAME"), rep.get_attr("OVS_VPORT_ATTR_NAME"),
OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")), OvsVport.type_to_str(rep.get_attr("OVS_VPORT_ATTR_TYPE")),
opts,
) )
) )
...@@ -2120,12 +2376,30 @@ def main(argv): ...@@ -2120,12 +2376,30 @@ def main(argv):
"--ptype", "--ptype",
type=str, type=str,
default="netdev", default="netdev",
choices=["netdev", "internal"], choices=["netdev", "internal", "geneve", "vxlan"],
help="Interface type (default netdev)", 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 = subparsers.add_parser("del-if")
delifcmd.add_argument("dpname", help="Datapath Name") delifcmd.add_argument("dpname", help="Datapath Name")
delifcmd.add_argument("delif", help="Interface name for adding") 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 = subparsers.add_parser("dump-flows")
dumpflcmd.add_argument("dumpdp", help="Datapath Name") dumpflcmd.add_argument("dumpdp", help="Datapath Name")
...@@ -2186,7 +2460,8 @@ def main(argv): ...@@ -2186,7 +2460,8 @@ def main(argv):
print("DP '%s' not found." % args.dpname) print("DP '%s' not found." % args.dpname)
return 1 return 1
dpindex = rep["dpifindex"] dpindex = rep["dpifindex"]
rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype) rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype,
args.dport, args.lwt)
msg = "vport '%s'" % args.addif msg = "vport '%s'" % args.addif
if rep and rep["header"]["error"] is None: if rep and rep["header"]["error"] is None:
msg += " added." msg += " added."
...@@ -2207,6 +2482,9 @@ def main(argv): ...@@ -2207,6 +2482,9 @@ def main(argv):
msg += " removed." msg += " removed."
else: else:
msg += " failed to remove." 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"): elif hasattr(args, "dumpdp"):
rep = ovsdp.info(args.dumpdp, 0) rep = ovsdp.info(args.dumpdp, 0)
if rep is None: if rep is None:
......
...@@ -842,25 +842,97 @@ setup_bridge() { ...@@ -842,25 +842,97 @@ setup_bridge() {
run_cmd ${ns_a} ip link set veth_A-C master br0 run_cmd ${ns_a} ip link set veth_A-C master br0
} }
setup_ovs_via_internal_utility() {
type="${1}"
a_addr="${2}"
b_addr="${3}"
dport="${4}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-if ovs_br0 ${type}_a -t ${type} || return 1
ports=$(python3 ./openvswitch/ovs-dpctl.py show)
br0_port=$(echo "$ports" | grep -E "\sovs_br0" | sed -e 's@port @@' | cut -d: -f1 | xargs)
type_a_port=$(echo "$ports" | grep ${type}_a | sed -e 's@port @@' | cut -d: -f1 | xargs)
veth_a_port=$(echo "$ports" | grep veth_A | sed -e 's@port @@' | cut -d: -f1 | xargs)
v4_a_tun="${prefix4}.${a_r1}.1"
v4_b_tun="${prefix4}.${b_r1}.1"
v6_a_tun="${prefix6}:${a_r1}::1"
v6_b_tun="${prefix6}:${b_r1}::1"
if [ "${v4_a_tun}" = "${a_addr}" ]; then
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),in_port(${veth_a_port}),eth(),eth_type(0x0800),ipv4()" \
"set(tunnel(tun_id=1,dst=${v4_b_tun},ttl=64,tp_dst=${dport},flags(df|csum))),${type_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),in_port(${veth_a_port}),eth(),eth_type(0x86dd),ipv6()" \
"set(tunnel(tun_id=1,dst=${v4_b_tun},ttl=64,tp_dst=${dport},flags(df|csum))),${type_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),tunnel(tun_id=1,src=${v4_b_tun},dst=${v4_a_tun}),in_port(${type_a_port}),eth(),eth_type(0x0800),ipv4()" \
"${veth_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),tunnel(tun_id=1,src=${v4_b_tun},dst=${v4_a_tun}),in_port(${type_a_port}),eth(),eth_type(0x86dd),ipv6()" \
"${veth_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),tunnel(tun_id=1,src=${v4_b_tun},dst=${v4_a_tun}),in_port(${type_a_port}),eth(),eth_type(0x0806),arp()" \
"${veth_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),in_port(${veth_a_port}),eth(),eth_type(0x0806),arp(sip=${veth4_c_addr},tip=${tunnel4_b_addr})" \
"set(tunnel(tun_id=1,dst=${v4_b_tun},ttl=64,tp_dst=${dport},flags(df|csum))),${type_a_port}"
else
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),in_port(${veth_a_port}),eth(),eth_type(0x0800),ipv4()" \
"set(tunnel(tun_id=1,ipv6_dst=${v6_b_tun},ttl=64,tp_dst=${dport},flags(df|csum))),${type_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),in_port(${veth_a_port}),eth(),eth_type(0x86dd),ipv6()" \
"set(tunnel(tun_id=1,ipv6_dst=${v6_b_tun},ttl=64,tp_dst=${dport},flags(df|csum))),${type_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),tunnel(tun_id=1,ipv6_src=${v6_b_tun},ipv6_dst=${v6_a_tun}),in_port(${type_a_port}),eth(),eth_type(0x0800),ipv4()" \
"${veth_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),tunnel(tun_id=1,ipv6_src=${v6_b_tun},ipv6_dst=${v6_a_tun}),in_port(${type_a_port}),eth(),eth_type(0x86dd),ipv6()" \
"${veth_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),tunnel(tun_id=1,ipv6_src=${v6_b_tun},ipv6_dst=${v6_a_tun}),in_port(${type_a_port}),eth(),eth_type(0x0806),arp()" \
"${veth_a_port}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-flow ovs_br0 \
"recirc_id(0),in_port(${veth_a_port}),eth(),eth_type(0x0806),arp(sip=${veth4_c_addr},tip=${tunnel4_b_addr})" \
"set(tunnel(tun_id=1,ipv6_dst=${v6_b_tun},ttl=64,tp_dst=${dport},flags(df|csum))),${type_a_port}"
fi
}
setup_ovs_via_vswitchd() {
type="${1}"
b_addr="${2}"
run_cmd ovs-vsctl add-port ovs_br0 ${type}_a -- \
set interface ${type}_a type=${type} \
options:remote_ip=${b_addr} options:key=1 options:csum=true || return 1
}
setup_ovs_vxlan_or_geneve() { setup_ovs_vxlan_or_geneve() {
type="${1}" type="${1}"
a_addr="${2}" a_addr="${2}"
b_addr="${3}" b_addr="${3}"
dport="6081"
if [ "${type}" = "vxlan" ]; then if [ "${type}" = "vxlan" ]; then
dport="4789"
opts="${opts} ttl 64 dstport 4789" opts="${opts} ttl 64 dstport 4789"
opts_b="local ${b_addr}" opts_b="local ${b_addr}"
fi fi
run_cmd ovs-vsctl add-port ovs_br0 ${type}_a -- \ setup_ovs_via_internal_utility "${type}" "${a_addr}" "${b_addr}" \
set interface ${type}_a type=${type} \ "${dport}" || \
options:remote_ip=${b_addr} options:key=1 options:csum=true || return 1 setup_ovs_via_vswitchd "${type}" "${b_addr}" || return 1
run_cmd ${ns_b} ip link add ${type}_b type ${type} id 1 ${opts_b} remote ${a_addr} ${opts} || return 1 run_cmd ${ns_b} ip link add ${type}_b type ${type} id 1 ${opts_b} remote ${a_addr} ${opts} || return 1
run_cmd ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ${type}_b run_cmd ${ns_b} ip addr add ${tunnel4_b_addr}/${tunnel4_mask} dev ${type}_b
run_cmd ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ${type}_b run_cmd ${ns_b} ip addr add ${tunnel6_b_addr}/${tunnel6_mask} dev ${type}_b
run_cmd ip link set ${type}_a up
run_cmd ${ns_b} ip link set ${type}_b up run_cmd ${ns_b} ip link set ${type}_b up
} }
...@@ -880,8 +952,24 @@ setup_ovs_vxlan6() { ...@@ -880,8 +952,24 @@ setup_ovs_vxlan6() {
setup_ovs_vxlan_or_geneve vxlan ${prefix6}:${a_r1}::1 ${prefix6}:${b_r1}::1 setup_ovs_vxlan_or_geneve vxlan ${prefix6}:${a_r1}::1 ${prefix6}:${b_r1}::1
} }
setup_ovs_br_internal() {
run_cmd python3 ./openvswitch/ovs-dpctl.py add-dp ovs_br0 || \
return 1
}
setup_ovs_br_vswitchd() {
run_cmd ovs-vsctl add-br ovs_br0 || return 1
}
setup_ovs_add_if() {
ifname="${1}"
run_cmd python3 ./openvswitch/ovs-dpctl.py add-if ovs_br0 \
"${ifname}" || \
run_cmd ovs-vsctl add-port ovs_br0 "${ifname}"
}
setup_ovs_bridge() { setup_ovs_bridge() {
run_cmd ovs-vsctl add-br ovs_br0 || return $ksft_skip setup_ovs_br_internal || setup_ovs_br_vswitchd || return $ksft_skip
run_cmd ip link set ovs_br0 up run_cmd ip link set ovs_br0 up
run_cmd ${ns_c} ip link add veth_C-A type veth peer name veth_A-C run_cmd ${ns_c} ip link add veth_C-A type veth peer name veth_A-C
...@@ -891,7 +979,7 @@ setup_ovs_bridge() { ...@@ -891,7 +979,7 @@ setup_ovs_bridge() {
run_cmd ${ns_c} ip link set veth_C-A up run_cmd ${ns_c} ip link set veth_C-A up
run_cmd ${ns_c} ip addr add ${veth4_c_addr}/${veth4_mask} dev veth_C-A run_cmd ${ns_c} ip addr add ${veth4_c_addr}/${veth4_mask} dev veth_C-A
run_cmd ${ns_c} ip addr add ${veth6_c_addr}/${veth6_mask} dev veth_C-A run_cmd ${ns_c} ip addr add ${veth6_c_addr}/${veth6_mask} dev veth_C-A
run_cmd ovs-vsctl add-port ovs_br0 veth_A-C setup_ovs_add_if veth_A-C
# Move veth_A-R1 to init # Move veth_A-R1 to init
run_cmd ${ns_a} ip link set veth_A-R1 netns 1 run_cmd ${ns_a} ip link set veth_A-R1 netns 1
...@@ -922,6 +1010,18 @@ trace() { ...@@ -922,6 +1010,18 @@ trace() {
sleep 1 sleep 1
} }
cleanup_del_ovs_internal() {
# squelch the output of the del-if commands since it can be wordy
python3 ./openvswitch/ovs-dpctl.py del-if ovs_br0 -d true vxlan_a >/dev/null 2>&1
python3 ./openvswitch/ovs-dpctl.py del-if ovs_br0 -d true geneve_a >/dev/null 2>&1
python3 ./openvswitch/ovs-dpctl.py del-dp ovs_br0 >/dev/null 2>&1
}
cleanup_del_ovs_vswitchd() {
ovs-vsctl --if-exists del-port vxlan_a 2>/dev/null
ovs-vsctl --if-exists del-br ovs_br0 2>/dev/null
}
cleanup() { cleanup() {
for pid in ${tcpdump_pids}; do for pid in ${tcpdump_pids}; do
kill ${pid} kill ${pid}
...@@ -940,10 +1040,10 @@ cleanup() { ...@@ -940,10 +1040,10 @@ cleanup() {
cleanup_all_ns cleanup_all_ns
ip link del veth_A-C 2>/dev/null ip link del veth_A-C 2>/dev/null
ip link del veth_A-R1 2>/dev/null ip link del veth_A-R1 2>/dev/null
ovs-vsctl --if-exists del-port vxlan_a 2>/dev/null cleanup_del_ovs_internal
ovs-vsctl --if-exists del-br ovs_br0 2>/dev/null cleanup_del_ovs_vswitchd
rm -f "$tmpoutfile" rm -f "$tmpoutfile"
} }
...@@ -1397,6 +1497,12 @@ test_pmtu_ipvX_over_ovs_vxlanY_or_geneveY_exception() { ...@@ -1397,6 +1497,12 @@ test_pmtu_ipvX_over_ovs_vxlanY_or_geneveY_exception() {
outer_family=${3} outer_family=${3}
ll_mtu=4000 ll_mtu=4000
if [ "${type}" = "vxlan" ]; then
tun_a="vxlan_sys_4789"
elif [ "${type}" = "geneve" ]; then
tun_a="genev_sys_6081"
fi
if [ ${outer_family} -eq 4 ]; then if [ ${outer_family} -eq 4 ]; then
setup namespaces routing ovs_bridge ovs_${type}4 || return $ksft_skip setup namespaces routing ovs_bridge ovs_${type}4 || return $ksft_skip
# IPv4 header UDP header VXLAN/GENEVE header Ethernet header # IPv4 header UDP header VXLAN/GENEVE header Ethernet header
...@@ -1407,17 +1513,11 @@ test_pmtu_ipvX_over_ovs_vxlanY_or_geneveY_exception() { ...@@ -1407,17 +1513,11 @@ test_pmtu_ipvX_over_ovs_vxlanY_or_geneveY_exception() {
exp_mtu=$((${ll_mtu} - 40 - 8 - 8 - 14)) exp_mtu=$((${ll_mtu} - 40 - 8 - 8 - 14))
fi fi
if [ "${type}" = "vxlan" ]; then trace "" ${type}_a "${ns_b}" ${type}_b \
tun_a="vxlan_sys_4789" "" veth_A-R1 "${ns_r1}" veth_R1-A \
elif [ "${type}" = "geneve" ]; then "${ns_b}" veth_B-R1 "${ns_r1}" veth_R1-B \
tun_a="genev_sys_6081" "" ovs_br0 "" veth-A_C \
fi "${ns_c}" veth_C-A "" "${tun_a}"
trace "" "${tun_a}" "${ns_b}" ${type}_b \
"" veth_A-R1 "${ns_r1}" veth_R1-A \
"${ns_b}" veth_B-R1 "${ns_r1}" veth_R1-B \
"" ovs_br0 "" veth-A-C \
"${ns_c}" veth_C-A
if [ ${family} -eq 4 ]; then if [ ${family} -eq 4 ]; then
ping=ping ping=ping
...@@ -1436,8 +1536,9 @@ test_pmtu_ipvX_over_ovs_vxlanY_or_geneveY_exception() { ...@@ -1436,8 +1536,9 @@ test_pmtu_ipvX_over_ovs_vxlanY_or_geneveY_exception() {
mtu "${ns_b}" veth_B-R1 ${ll_mtu} mtu "${ns_b}" veth_B-R1 ${ll_mtu}
mtu "${ns_r1}" veth_R1-B ${ll_mtu} mtu "${ns_r1}" veth_R1-B ${ll_mtu}
mtu "" ${tun_a} $((${ll_mtu} + 1000)) mtu "" ${tun_a} $((${ll_mtu} + 1000)) 2>/dev/null || \
mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000)) mtu "" ${type}_a $((${ll_mtu} + 1000)) 2>/dev/null
mtu "${ns_b}" ${type}_b $((${ll_mtu} + 1000))
run_cmd ${ns_c} ${ping} -q -M want -i 0.1 -c 20 -s $((${ll_mtu} + 500)) ${dst} || return 1 run_cmd ${ns_c} ${ping} -q -M want -i 0.1 -c 20 -s $((${ll_mtu} + 500)) ${dst} || return 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment