Commit 9feac87b authored by Aaron Conole's avatar Aaron Conole Committed by David S. Miller

selftests: openvswitch: add support for upcall testing

The upcall socket interface can be exercised now to make sure that
future feature adjustments to the field can maintain backwards
compatibility.
Signed-off-by: default avatarAaron Conole <aconole@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e52b07aa
...@@ -11,7 +11,8 @@ VERBOSE=0 ...@@ -11,7 +11,8 @@ VERBOSE=0
TRACING=0 TRACING=0
tests=" tests="
netlink_checks ovsnl: validate netlink attrs and settings" netlink_checks ovsnl: validate netlink attrs and settings
upcall_interfaces ovs: test the upcall interfaces"
info() { info() {
[ $VERBOSE = 0 ] || echo $* [ $VERBOSE = 0 ] || echo $*
...@@ -72,7 +73,15 @@ ovs_add_dp () { ...@@ -72,7 +73,15 @@ ovs_add_dp () {
ovs_add_if () { ovs_add_if () {
info "Adding IF to DP: br:$2 if:$3" info "Adding IF to DP: br:$2 if:$3"
ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py add-if "$2" "$3" || return 1 if [ "$4" != "-u" ]; then
ovs_sbx "$1" python3 $ovs_base/ovs-dpctl.py add-if "$2" "$3" \
|| return 1
else
python3 $ovs_base/ovs-dpctl.py add-if \
-u "$2" "$3" >$ovs_dir/$3.out 2>$ovs_dir/$3.err &
pid=$!
on_exit "ovs_sbx $1 kill -TERM $pid 2>/dev/null"
fi
} }
ovs_del_if () { ovs_del_if () {
...@@ -106,7 +115,12 @@ ovs_add_netns_and_veths () { ...@@ -106,7 +115,12 @@ ovs_add_netns_and_veths () {
|| return 1 || return 1
fi fi
if [ "$7" != "-u" ]; then
ovs_add_if "$1" "$2" "$4" || return 1 ovs_add_if "$1" "$2" "$4" || return 1
else
ovs_add_if "$1" "$2" "$4" -u || return 1
fi
[ $TRACING -eq 1 ] && ovs_netns_spawn_daemon "$1" "$ns" \ [ $TRACING -eq 1 ] && ovs_netns_spawn_daemon "$1" "$ns" \
tcpdump -i any -s 65535 tcpdump -i any -s 65535
...@@ -159,6 +173,24 @@ test_netlink_checks () { ...@@ -159,6 +173,24 @@ test_netlink_checks () {
return 0 return 0
} }
test_upcall_interfaces() {
sbx_add "test_upcall_interfaces" || return 1
info "setting up new DP"
ovs_add_dp "test_upcall_interfaces" ui0 -V 2:1 || return 1
ovs_add_netns_and_veths "test_upcall_interfaces" ui0 upc left0 l0 \
172.31.110.1/24 -u || return 1
sleep 1
info "sending arping"
ip netns exec upc arping -I l0 172.31.110.20 -c 1 \
>$ovs_dir/arping.stdout 2>$ovs_dir/arping.stderr
grep -E "MISS upcall\[0/yes\]: .*arp\(sip=172.31.110.1,tip=172.31.110.20,op=1,sha=" $ovs_dir/left0.out >/dev/null 2>&1 || return 1
return 0
}
run_test() { run_test() {
( (
tname="$1" tname="$1"
......
...@@ -8,6 +8,8 @@ import argparse ...@@ -8,6 +8,8 @@ import argparse
import errno import errno
import ipaddress import ipaddress
import logging import logging
import multiprocessing
import struct
import sys import sys
import time import time
...@@ -926,6 +928,51 @@ class ovskey(nla): ...@@ -926,6 +928,51 @@ class ovskey(nla):
return 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): class OvsDatapath(GenericNetlinkSocket):
OVS_DP_F_VPORT_PIDS = 1 << 1 OVS_DP_F_VPORT_PIDS = 1 << 1
OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3 OVS_DP_F_DISPATCH_UPCALL_PER_CPU = 1 << 3
...@@ -989,7 +1036,9 @@ class OvsDatapath(GenericNetlinkSocket): ...@@ -989,7 +1036,9 @@ class OvsDatapath(GenericNetlinkSocket):
return reply return reply
def create(self, dpname, shouldUpcall=False, versionStr=None): def create(
self, dpname, shouldUpcall=False, versionStr=None, p=OvsPacket()
):
msg = OvsDatapath.dp_cmd_msg() msg = OvsDatapath.dp_cmd_msg()
msg["cmd"] = OVS_DP_CMD_NEW msg["cmd"] = OVS_DP_CMD_NEW
if versionStr is None: if versionStr is None:
...@@ -1004,11 +1053,18 @@ class OvsDatapath(GenericNetlinkSocket): ...@@ -1004,11 +1053,18 @@ class OvsDatapath(GenericNetlinkSocket):
if versionStr is not None and versionStr.find(":") != -1: if versionStr is not None and versionStr.find(":") != -1:
dpfeatures = int(versionStr.split(":")[1], 0) dpfeatures = int(versionStr.split(":")[1], 0)
else: else:
dpfeatures = OvsDatapath.OVS_DP_F_VPORT_PIDS 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]) msg["attrs"].append(["OVS_DP_ATTR_USER_FEATURES", dpfeatures])
if not shouldUpcall: if not shouldUpcall:
msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", 0]) msg["attrs"].append(["OVS_DP_ATTR_UPCALL_PID", [0]])
try: try:
reply = self.nlm_request( reply = self.nlm_request(
...@@ -1104,9 +1160,10 @@ class OvsVport(GenericNetlinkSocket): ...@@ -1104,9 +1160,10 @@ class OvsVport(GenericNetlinkSocket):
return OvsVport.OVS_VPORT_TYPE_GENEVE return OvsVport.OVS_VPORT_TYPE_GENEVE
raise ValueError("Unknown vport type: '%s'" % vport_type) raise ValueError("Unknown vport type: '%s'" % vport_type)
def __init__(self): def __init__(self, packet=OvsPacket()):
GenericNetlinkSocket.__init__(self) GenericNetlinkSocket.__init__(self)
self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg) self.bind(OVS_VPORT_FAMILY, OvsVport.ovs_vport_msg)
self.upcall_packet = packet
def info(self, vport_name, dpifindex=0, portno=None): def info(self, vport_name, dpifindex=0, portno=None):
msg = OvsVport.ovs_vport_msg() msg = OvsVport.ovs_vport_msg()
...@@ -1144,7 +1201,37 @@ class OvsVport(GenericNetlinkSocket): ...@@ -1144,7 +1201,37 @@ class OvsVport(GenericNetlinkSocket):
msg["attrs"].append(["OVS_VPORT_ATTR_TYPE", port_type]) 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(["OVS_VPORT_ATTR_UPCALL_PID", [self.pid]]) 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: try:
reply = self.nlm_request( reply = self.nlm_request(
...@@ -1176,6 +1263,9 @@ class OvsVport(GenericNetlinkSocket): ...@@ -1176,6 +1263,9 @@ class OvsVport(GenericNetlinkSocket):
raise ne raise ne
return reply return reply
def upcall_handler(self, handler=None):
self.upcall_packet.upcall_handler(handler)
class OvsFlow(GenericNetlinkSocket): class OvsFlow(GenericNetlinkSocket):
class ovs_flow_msg(ovs_dp_msg): class ovs_flow_msg(ovs_dp_msg):
...@@ -1305,6 +1395,24 @@ class OvsFlow(GenericNetlinkSocket): ...@@ -1305,6 +1395,24 @@ class OvsFlow(GenericNetlinkSocket):
raise ne raise ne
return rep 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()): def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME") dp_name = dp_lookup_rep.get_attr("OVS_DP_ATTR_NAME")
...@@ -1385,6 +1493,12 @@ def main(argv): ...@@ -1385,6 +1493,12 @@ def main(argv):
addifcmd = subparsers.add_parser("add-if") addifcmd = subparsers.add_parser("add-if")
addifcmd.add_argument("dpname", help="Datapath Name") addifcmd.add_argument("dpname", help="Datapath Name")
addifcmd.add_argument("addif", help="Interface name for adding") 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( addifcmd.add_argument(
"-t", "-t",
"--ptype", "--ptype",
...@@ -1406,8 +1520,9 @@ def main(argv): ...@@ -1406,8 +1520,9 @@ def main(argv):
if args.verbose > 1: if args.verbose > 1:
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
ovspk = OvsPacket()
ovsdp = OvsDatapath() ovsdp = OvsDatapath()
ovsvp = OvsVport() ovsvp = OvsVport(ovspk)
ovsflow = OvsFlow() ovsflow = OvsFlow()
ndb = NDB() ndb = NDB()
...@@ -1430,11 +1545,13 @@ def main(argv): ...@@ -1430,11 +1545,13 @@ def main(argv):
msg += ":'%s'" % args.showdp msg += ":'%s'" % args.showdp
print(msg) print(msg)
elif hasattr(args, "adddp"): elif hasattr(args, "adddp"):
rep = ovsdp.create(args.adddp, args.upcall, args.versioning) rep = ovsdp.create(args.adddp, args.upcall, args.versioning, ovspk)
if rep is None: if rep is None:
print("DP '%s' already exists" % args.adddp) print("DP '%s' already exists" % args.adddp)
else: else:
print("DP '%s' added" % args.adddp) print("DP '%s' added" % args.adddp)
if args.upcall:
ovspk.upcall_handler(ovsflow)
elif hasattr(args, "deldp"): elif hasattr(args, "deldp"):
ovsdp.destroy(args.deldp) ovsdp.destroy(args.deldp)
elif hasattr(args, "addif"): elif hasattr(args, "addif"):
...@@ -1442,12 +1559,17 @@ def main(argv): ...@@ -1442,12 +1559,17 @@ def main(argv):
if rep is None: if rep is None:
print("DP '%s' not found." % args.dpname) print("DP '%s' not found." % args.dpname)
return 1 return 1
dpindex = rep["dpifindex"]
rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype) rep = ovsvp.attach(rep["dpifindex"], args.addif, args.ptype)
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."
else: else:
msg += " failed to add." 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"): elif hasattr(args, "delif"):
rep = ovsdp.info(args.dpname, 0) rep = ovsdp.info(args.dpname, 0)
if rep is None: if rep is None:
......
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