Skip to content

Commit ea2f7da

Browse files
Florian Westphalummakynes
Florian Westphal
authored andcommitted
selftests: netfilter: extend nfqueue test case
add a test with re-queueing: usespace doesn't pass accept verdict, but tells to re-queue to another nf_queue instance. Also, make the second nf-queue program use non-gso mode, kernel will have to perform software segmentation. Lastly, do not queue every packet, just one per second, and add delay when re-injecting the packet to the kernel. Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 874fb9e commit ea2f7da

File tree

2 files changed

+109
-22
lines changed

2 files changed

+109
-22
lines changed

tools/testing/selftests/netfilter/nf-queue.c

+52-9
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,20 @@
1717

1818
struct options {
1919
bool count_packets;
20+
bool gso_enabled;
2021
int verbose;
2122
unsigned int queue_num;
2223
unsigned int timeout;
24+
uint32_t verdict;
25+
uint32_t delay_ms;
2326
};
2427

2528
static unsigned int queue_stats[5];
2629
static struct options opts;
2730

2831
static void help(const char *p)
2932
{
30-
printf("Usage: %s [-c|-v [-vv] ] [-t timeout] [-q queue_num]\n", p);
33+
printf("Usage: %s [-c|-v [-vv] ] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\n", p);
3134
}
3235

3336
static int parse_attr_cb(const struct nlattr *attr, void *data)
@@ -162,7 +165,7 @@ nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
162165
}
163166

164167
static struct nlmsghdr *
165-
nfq_build_verdict(char *buf, int id, int queue_num, int verd)
168+
nfq_build_verdict(char *buf, int id, int queue_num, uint32_t verd)
166169
{
167170
struct nfqnl_msg_verdict_hdr vh = {
168171
.verdict = htonl(verd),
@@ -189,9 +192,6 @@ static void print_stats(void)
189192
unsigned int last, total;
190193
int i;
191194

192-
if (!opts.count_packets)
193-
return;
194-
195195
total = 0;
196196
last = queue_stats[0];
197197

@@ -234,7 +234,8 @@ struct mnl_socket *open_queue(void)
234234

235235
nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
236236

237-
flags = NFQA_CFG_F_GSO | NFQA_CFG_F_UID_GID;
237+
flags = opts.gso_enabled ? NFQA_CFG_F_GSO : 0;
238+
flags |= NFQA_CFG_F_UID_GID;
238239
mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(flags));
239240
mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(flags));
240241

@@ -255,6 +256,17 @@ struct mnl_socket *open_queue(void)
255256
return nl;
256257
}
257258

259+
static void sleep_ms(uint32_t delay)
260+
{
261+
struct timespec ts = { .tv_sec = delay / 1000 };
262+
263+
delay %= 1000;
264+
265+
ts.tv_nsec = delay * 1000llu * 1000llu;
266+
267+
nanosleep(&ts, NULL);
268+
}
269+
258270
static int mainloop(void)
259271
{
260272
unsigned int buflen = 64 * 1024 + MNL_SOCKET_BUFFER_SIZE;
@@ -278,7 +290,7 @@ static int mainloop(void)
278290

279291
ret = mnl_socket_recvfrom(nl, buf, buflen);
280292
if (ret == -1) {
281-
if (errno == ENOBUFS)
293+
if (errno == ENOBUFS || errno == EINTR)
282294
continue;
283295

284296
if (errno == EAGAIN) {
@@ -298,7 +310,10 @@ static int mainloop(void)
298310
}
299311

300312
id = ret - MNL_CB_OK;
301-
nlh = nfq_build_verdict(buf, id, opts.queue_num, NF_ACCEPT);
313+
if (opts.delay_ms)
314+
sleep_ms(opts.delay_ms);
315+
316+
nlh = nfq_build_verdict(buf, id, opts.queue_num, opts.verdict);
302317
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
303318
perror("mnl_socket_sendto");
304319
exit(EXIT_FAILURE);
@@ -314,7 +329,7 @@ static void parse_opts(int argc, char **argv)
314329
{
315330
int c;
316331

317-
while ((c = getopt(argc, argv, "chvt:q:")) != -1) {
332+
while ((c = getopt(argc, argv, "chvt:q:Q:d:G")) != -1) {
318333
switch (c) {
319334
case 'c':
320335
opts.count_packets = true;
@@ -328,20 +343,48 @@ static void parse_opts(int argc, char **argv)
328343
if (opts.queue_num > 0xffff)
329344
opts.queue_num = 0;
330345
break;
346+
case 'Q':
347+
opts.verdict = atoi(optarg);
348+
if (opts.verdict > 0xffff) {
349+
fprintf(stderr, "Expected destination queue number\n");
350+
exit(1);
351+
}
352+
353+
opts.verdict <<= 16;
354+
opts.verdict |= NF_QUEUE;
355+
break;
356+
case 'd':
357+
opts.delay_ms = atoi(optarg);
358+
if (opts.delay_ms == 0) {
359+
fprintf(stderr, "Expected nonzero delay (in milliseconds)\n");
360+
exit(1);
361+
}
362+
break;
331363
case 't':
332364
opts.timeout = atoi(optarg);
333365
break;
366+
case 'G':
367+
opts.gso_enabled = false;
368+
break;
334369
case 'v':
335370
opts.verbose++;
336371
break;
337372
}
338373
}
374+
375+
if (opts.verdict != NF_ACCEPT && (opts.verdict >> 16 == opts.queue_num)) {
376+
fprintf(stderr, "Cannot use same destination and source queue\n");
377+
exit(1);
378+
}
339379
}
340380

341381
int main(int argc, char *argv[])
342382
{
343383
int ret;
344384

385+
opts.verdict = NF_ACCEPT;
386+
opts.gso_enabled = true;
387+
345388
parse_opts(argc, argv);
346389

347390
ret = mainloop();

tools/testing/selftests/netfilter/nft_queue.sh

+57-13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ sfx=$(mktemp -u "XXXXXXXX")
1212
ns1="ns1-$sfx"
1313
ns2="ns2-$sfx"
1414
nsrouter="nsrouter-$sfx"
15+
timeout=4
1516

1617
cleanup()
1718
{
@@ -20,6 +21,7 @@ cleanup()
2021
ip netns del ${nsrouter}
2122
rm -f "$TMPFILE0"
2223
rm -f "$TMPFILE1"
24+
rm -f "$TMPFILE2" "$TMPFILE3"
2325
}
2426

2527
nft --version > /dev/null 2>&1
@@ -42,6 +44,8 @@ fi
4244

4345
TMPFILE0=$(mktemp)
4446
TMPFILE1=$(mktemp)
47+
TMPFILE2=$(mktemp)
48+
TMPFILE3=$(mktemp)
4549
trap cleanup EXIT
4650

4751
ip netns add ${ns1}
@@ -83,7 +87,7 @@ load_ruleset() {
8387
local name=$1
8488
local prio=$2
8589

86-
ip netns exec ${nsrouter} nft -f - <<EOF
90+
ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
8791
table inet $name {
8892
chain nfq {
8993
ip protocol icmp queue bypass
@@ -118,7 +122,7 @@ EOF
118122
load_counter_ruleset() {
119123
local prio=$1
120124

121-
ip netns exec ${nsrouter} nft -f - <<EOF
125+
ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
122126
table inet countrules {
123127
chain pre {
124128
type filter hook prerouting priority $prio; policy accept;
@@ -175,7 +179,7 @@ test_ping_router() {
175179
test_queue_blackhole() {
176180
local proto=$1
177181

178-
ip netns exec ${nsrouter} nft -f - <<EOF
182+
ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
179183
table $proto blackh {
180184
chain forward {
181185
type filter hook forward priority 0; policy accept;
@@ -184,10 +188,10 @@ table $proto blackh {
184188
}
185189
EOF
186190
if [ $proto = "ip" ] ;then
187-
ip netns exec ${ns1} ping -c 1 -q 10.0.2.99 > /dev/null
191+
ip netns exec ${ns1} ping -W 2 -c 1 -q 10.0.2.99 > /dev/null
188192
lret=$?
189193
elif [ $proto = "ip6" ]; then
190-
ip netns exec ${ns1} ping -c 1 -q dead:2::99 > /dev/null
194+
ip netns exec ${ns1} ping -W 2 -c 1 -q dead:2::99 > /dev/null
191195
lret=$?
192196
else
193197
lret=111
@@ -214,8 +218,8 @@ test_queue()
214218
local last=""
215219

216220
# spawn nf-queue listeners
217-
ip netns exec ${nsrouter} ./nf-queue -c -q 0 -t 3 > "$TMPFILE0" &
218-
ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t 3 > "$TMPFILE1" &
221+
ip netns exec ${nsrouter} ./nf-queue -c -q 0 -t $timeout > "$TMPFILE0" &
222+
ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE1" &
219223
sleep 1
220224
test_ping
221225
ret=$?
@@ -250,11 +254,11 @@ test_queue()
250254

251255
test_tcp_forward()
252256
{
253-
ip netns exec ${nsrouter} ./nf-queue -q 2 -t 10 &
257+
ip netns exec ${nsrouter} ./nf-queue -q 2 -t $timeout &
254258
local nfqpid=$!
255259

256260
tmpfile=$(mktemp) || exit 1
257-
dd conv=sparse status=none if=/dev/zero bs=1M count=100 of=$tmpfile
261+
dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile
258262
ip netns exec ${ns2} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null &
259263
local rpid=$!
260264

@@ -270,15 +274,13 @@ test_tcp_forward()
270274

271275
test_tcp_localhost()
272276
{
273-
tc -net "${nsrouter}" qdisc add dev lo root netem loss random 1%
274-
275277
tmpfile=$(mktemp) || exit 1
276278

277-
dd conv=sparse status=none if=/dev/zero bs=1M count=900 of=$tmpfile
279+
dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile
278280
ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null &
279281
local rpid=$!
280282

281-
ip netns exec ${nsrouter} ./nf-queue -q 3 -t 30 &
283+
ip netns exec ${nsrouter} ./nf-queue -q 3 -t $timeout &
282284
local nfqpid=$!
283285

284286
sleep 1
@@ -287,6 +289,47 @@ test_tcp_localhost()
287289

288290
wait $rpid
289291
[ $? -eq 0 ] && echo "PASS: tcp via loopback"
292+
wait 2>/dev/null
293+
}
294+
295+
test_tcp_localhost_requeue()
296+
{
297+
ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF
298+
flush ruleset
299+
table inet filter {
300+
chain output {
301+
type filter hook output priority 0; policy accept;
302+
tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0
303+
}
304+
chain post {
305+
type filter hook postrouting priority 0; policy accept;
306+
tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0
307+
}
308+
}
309+
EOF
310+
tmpfile=$(mktemp) || exit 1
311+
dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile
312+
ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null &
313+
local rpid=$!
314+
315+
ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE2" &
316+
317+
# nfqueue 1 will be called via output hook. But this time,
318+
# re-queue the packet to nfqueue program on queue 2.
319+
ip netns exec ${nsrouter} ./nf-queue -G -d 150 -c -q 0 -Q 1 -t $timeout > "$TMPFILE3" &
320+
321+
sleep 1
322+
ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null
323+
rm -f "$tmpfile"
324+
325+
wait
326+
327+
if ! diff -u "$TMPFILE2" "$TMPFILE3" ; then
328+
echo "FAIL: lost packets during requeue?!" 1>&2
329+
return
330+
fi
331+
332+
echo "PASS: tcp via loopback and re-queueing"
290333
}
291334

292335
ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null
@@ -328,5 +371,6 @@ test_queue 20
328371

329372
test_tcp_forward
330373
test_tcp_localhost
374+
test_tcp_localhost_requeue
331375

332376
exit $ret

0 commit comments

Comments
 (0)