Skip to content

Commit 30d772a

Browse files
amorenozkuba-moo
authored andcommitted
selftests: openvswitch: add psample test
Add a test to verify sampling packets via psample works. In order to do that, create a subcommand in ovs-dpctl.py to listen to on the psample multicast group and print samples. Reviewed-by: Aaron Conole <[email protected]> Tested-by: Ilya Maximets <[email protected]> Signed-off-by: Adrian Moreno <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent b192bf1 commit 30d772a

File tree

2 files changed

+182
-6
lines changed

2 files changed

+182
-6
lines changed

tools/testing/selftests/net/openvswitch/openvswitch.sh

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ tests="
2020
nat_related_v4 ip4-nat-related: ICMP related matches work with SNAT
2121
netlink_checks ovsnl: validate netlink attrs and settings
2222
upcall_interfaces ovs: test the upcall interfaces
23-
drop_reason drop: test drop reasons are emitted"
23+
drop_reason drop: test drop reasons are emitted
24+
psample psample: Sampling packets with psample"
2425

2526
info() {
2627
[ "${ovs_dir}" != "" ] &&
@@ -105,12 +106,21 @@ ovs_netns_spawn_daemon() {
105106
shift
106107
netns=$1
107108
shift
108-
info "spawning cmd: $*"
109-
ip netns exec $netns $* >> $ovs_dir/stdout 2>> $ovs_dir/stderr &
109+
if [ "$netns" == "_default" ]; then
110+
$* >> $ovs_dir/stdout 2>> $ovs_dir/stderr &
111+
else
112+
ip netns exec $netns $* >> $ovs_dir/stdout 2>> $ovs_dir/stderr &
113+
fi
110114
pid=$!
111115
ovs_sbx "$sbx" on_exit "kill -TERM $pid 2>/dev/null"
112116
}
113117

118+
ovs_spawn_daemon() {
119+
sbx=$1
120+
shift
121+
ovs_netns_spawn_daemon $sbx "_default" $*
122+
}
123+
114124
ovs_add_netns_and_veths () {
115125
info "Adding netns attached: sbx:$1 dp:$2 {$3, $4, $5}"
116126
ovs_sbx "$1" ip netns add "$3" || return 1
@@ -173,6 +183,19 @@ ovs_drop_reason_count()
173183
return `echo "$perf_output" | grep "$pattern" | wc -l`
174184
}
175185

186+
ovs_test_flow_fails () {
187+
ERR_MSG="Flow actions may not be safe on all matching packets"
188+
189+
PRE_TEST=$(dmesg | grep -c "${ERR_MSG}")
190+
ovs_add_flow $@ &> /dev/null $@ && return 1
191+
POST_TEST=$(dmesg | grep -c "${ERR_MSG}")
192+
193+
if [ "$PRE_TEST" == "$POST_TEST" ]; then
194+
return 1
195+
fi
196+
return 0
197+
}
198+
176199
usage() {
177200
echo
178201
echo "$0 [OPTIONS] [TEST]..."
@@ -187,6 +210,92 @@ usage() {
187210
exit 1
188211
}
189212

213+
214+
# psample test
215+
# - use psample to observe packets
216+
test_psample() {
217+
sbx_add "test_psample" || return $?
218+
219+
# Add a datapath with per-vport dispatching.
220+
ovs_add_dp "test_psample" psample -V 2:1 || return 1
221+
222+
info "create namespaces"
223+
ovs_add_netns_and_veths "test_psample" "psample" \
224+
client c0 c1 172.31.110.10/24 -u || return 1
225+
ovs_add_netns_and_veths "test_psample" "psample" \
226+
server s0 s1 172.31.110.20/24 -u || return 1
227+
228+
# Check if psample actions can be configured.
229+
ovs_add_flow "test_psample" psample \
230+
'in_port(1),eth(),eth_type(0x0806),arp()' 'psample(group=1)' &> /dev/null
231+
if [ $? == 1 ]; then
232+
info "no support for psample - skipping"
233+
ovs_exit_sig
234+
return $ksft_skip
235+
fi
236+
237+
ovs_del_flows "test_psample" psample
238+
239+
# Test action verification.
240+
OLDIFS=$IFS
241+
IFS='*'
242+
min_key='in_port(1),eth(),eth_type(0x0800),ipv4()'
243+
for testcase in \
244+
"cookie to large"*"psample(group=1,cookie=1615141312111009080706050403020100)" \
245+
"no group with cookie"*"psample(cookie=abcd)" \
246+
"no group"*"psample()";
247+
do
248+
set -- $testcase;
249+
ovs_test_flow_fails "test_psample" psample $min_key $2
250+
if [ $? == 1 ]; then
251+
info "failed - $1"
252+
return 1
253+
fi
254+
done
255+
IFS=$OLDIFS
256+
257+
ovs_del_flows "test_psample" psample
258+
# Allow ARP
259+
ovs_add_flow "test_psample" psample \
260+
'in_port(1),eth(),eth_type(0x0806),arp()' '2' || return 1
261+
ovs_add_flow "test_psample" psample \
262+
'in_port(2),eth(),eth_type(0x0806),arp()' '1' || return 1
263+
264+
# Sample first 14 bytes of all traffic.
265+
ovs_add_flow "test_psample" psample \
266+
"in_port(1),eth(),eth_type(0x0800),ipv4()" \
267+
"trunc(14),psample(group=1,cookie=c0ffee),2"
268+
269+
# Sample all traffic. In this case, use a sample() action with both
270+
# psample and an upcall emulating simultaneous local sampling and
271+
# sFlow / IPFIX.
272+
nlpid=$(grep -E "listening on upcall packet handler" \
273+
$ovs_dir/s0.out | cut -d ":" -f 2 | tr -d ' ')
274+
275+
ovs_add_flow "test_psample" psample \
276+
"in_port(2),eth(),eth_type(0x0800),ipv4()" \
277+
"sample(sample=100%,actions(psample(group=2,cookie=eeff0c),userspace(pid=${nlpid},userdata=eeff0c))),1"
278+
279+
# Record psample data.
280+
ovs_spawn_daemon "test_psample" python3 $ovs_base/ovs-dpctl.py psample-events
281+
282+
# Send a single ping.
283+
sleep 1
284+
ovs_sbx "test_psample" ip netns exec client ping -I c1 172.31.110.20 -c 1 || return 1
285+
sleep 1
286+
287+
# We should have received one userspace action upcall and 2 psample packets.
288+
grep -E "userspace action command" $ovs_dir/s0.out >/dev/null 2>&1 || return 1
289+
290+
# client -> server samples should only contain the first 14 bytes of the packet.
291+
grep -E "rate:4294967295,group:1,cookie:c0ffee data:[0-9a-f]{28}$" \
292+
$ovs_dir/stdout >/dev/null 2>&1 || return 1
293+
grep -E "rate:4294967295,group:2,cookie:eeff0c" \
294+
$ovs_dir/stdout >/dev/null 2>&1 || return 1
295+
296+
return 0
297+
}
298+
190299
# drop_reason test
191300
# - drop packets and verify the right drop reason is reported
192301
test_drop_reason() {

tools/testing/selftests/net/openvswitch/ovs-dpctl.py

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
from pyroute2.netlink import genlmsg
2929
from pyroute2.netlink import nla
3030
from pyroute2.netlink import nlmsg_atoms
31+
from pyroute2.netlink.event import EventSocket
3132
from pyroute2.netlink.exceptions import NetlinkError
3233
from pyroute2.netlink.generic import GenericNetlinkSocket
34+
from pyroute2.netlink.nlsocket import Marshal
3335
import pyroute2
3436
import pyroute2.iproute
3537

@@ -2460,10 +2462,70 @@ def miss(self, packetmsg):
24602462
print("MISS upcall[%d/%s]: %s" % (seq, pktpres, keystr), flush=True)
24612463

24622464
def execute(self, packetmsg):
2463-
print("userspace execute command")
2465+
print("userspace execute command", flush=True)
24642466

24652467
def action(self, packetmsg):
2466-
print("userspace action command")
2468+
print("userspace action command", flush=True)
2469+
2470+
2471+
class psample_sample(genlmsg):
2472+
nla_map = (
2473+
("PSAMPLE_ATTR_IIFINDEX", "none"),
2474+
("PSAMPLE_ATTR_OIFINDEX", "none"),
2475+
("PSAMPLE_ATTR_ORIGSIZE", "none"),
2476+
("PSAMPLE_ATTR_SAMPLE_GROUP", "uint32"),
2477+
("PSAMPLE_ATTR_GROUP_SEQ", "none"),
2478+
("PSAMPLE_ATTR_SAMPLE_RATE", "uint32"),
2479+
("PSAMPLE_ATTR_DATA", "array(uint8)"),
2480+
("PSAMPLE_ATTR_GROUP_REFCOUNT", "none"),
2481+
("PSAMPLE_ATTR_TUNNEL", "none"),
2482+
("PSAMPLE_ATTR_PAD", "none"),
2483+
("PSAMPLE_ATTR_OUT_TC", "none"),
2484+
("PSAMPLE_ATTR_OUT_TC_OCC", "none"),
2485+
("PSAMPLE_ATTR_LATENCY", "none"),
2486+
("PSAMPLE_ATTR_TIMESTAMP", "none"),
2487+
("PSAMPLE_ATTR_PROTO", "none"),
2488+
("PSAMPLE_ATTR_USER_COOKIE", "array(uint8)"),
2489+
)
2490+
2491+
def dpstr(self):
2492+
fields = []
2493+
data = ""
2494+
for (attr, value) in self["attrs"]:
2495+
if attr == "PSAMPLE_ATTR_SAMPLE_GROUP":
2496+
fields.append("group:%d" % value)
2497+
if attr == "PSAMPLE_ATTR_SAMPLE_RATE":
2498+
fields.append("rate:%d" % value)
2499+
if attr == "PSAMPLE_ATTR_USER_COOKIE":
2500+
value = "".join(format(x, "02x") for x in value)
2501+
fields.append("cookie:%s" % value)
2502+
if attr == "PSAMPLE_ATTR_DATA" and len(value) > 0:
2503+
data = "data:%s" % "".join(format(x, "02x") for x in value)
2504+
2505+
return ("%s %s" % (",".join(fields), data)).strip()
2506+
2507+
2508+
class psample_msg(Marshal):
2509+
PSAMPLE_CMD_SAMPLE = 0
2510+
PSAMPLE_CMD_GET_GROUP = 1
2511+
PSAMPLE_CMD_NEW_GROUP = 2
2512+
PSAMPLE_CMD_DEL_GROUP = 3
2513+
PSAMPLE_CMD_SET_FILTER = 4
2514+
msg_map = {PSAMPLE_CMD_SAMPLE: psample_sample}
2515+
2516+
2517+
class PsampleEvent(EventSocket):
2518+
genl_family = "psample"
2519+
mcast_groups = ["packets"]
2520+
marshal_class = psample_msg
2521+
2522+
def read_samples(self):
2523+
while True:
2524+
try:
2525+
for msg in self.get():
2526+
print(msg.dpstr(), flush=True)
2527+
except NetlinkError as ne:
2528+
raise ne
24672529

24682530

24692531
def print_ovsdp_full(dp_lookup_rep, ifindex, ndb=NDB(), vpl=OvsVport()):
@@ -2530,7 +2592,7 @@ def main(argv):
25302592
help="Increment 'verbose' output counter.",
25312593
default=0,
25322594
)
2533-
subparsers = parser.add_subparsers()
2595+
subparsers = parser.add_subparsers(dest="subcommand")
25342596

25352597
showdpcmd = subparsers.add_parser("show")
25362598
showdpcmd.add_argument(
@@ -2605,6 +2667,8 @@ def main(argv):
26052667
delfscmd = subparsers.add_parser("del-flows")
26062668
delfscmd.add_argument("flsbr", help="Datapath name")
26072669

2670+
subparsers.add_parser("psample-events")
2671+
26082672
args = parser.parse_args()
26092673

26102674
if args.verbose > 0:
@@ -2619,6 +2683,9 @@ def main(argv):
26192683

26202684
sys.setrecursionlimit(100000)
26212685

2686+
if args.subcommand == "psample-events":
2687+
PsampleEvent().read_samples()
2688+
26222689
if hasattr(args, "showdp"):
26232690
found = False
26242691
for iface in ndb.interfaces:

0 commit comments

Comments
 (0)