Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
ea2f7da1 874fb9e2

+109 -22
+52 -9
tools/testing/selftests/netfilter/nf-queue.c
··· 17 17 18 18 struct options { 19 19 bool count_packets; 20 + bool gso_enabled; 20 21 int verbose; 21 22 unsigned int queue_num; 22 23 unsigned int timeout; 24 + uint32_t verdict; 25 + uint32_t delay_ms; 23 26 }; 24 27 25 28 static unsigned int queue_stats[5]; ··· 30 27 31 28 static void help(const char *p) 32 29 { 33 - printf("Usage: %s [-c|-v [-vv] ] [-t timeout] [-q queue_num]\n", p); 30 + printf("Usage: %s [-c|-v [-vv] ] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\n", p); 34 31 } 35 32 36 33 static int parse_attr_cb(const struct nlattr *attr, void *data) ··· 165 162 } 166 163 167 164 static struct nlmsghdr * 168 - nfq_build_verdict(char *buf, int id, int queue_num, int verd) 165 + nfq_build_verdict(char *buf, int id, int queue_num, uint32_t verd) 169 166 { 170 167 struct nfqnl_msg_verdict_hdr vh = { 171 168 .verdict = htonl(verd), ··· 191 188 { 192 189 unsigned int last, total; 193 190 int i; 194 - 195 - if (!opts.count_packets) 196 - return; 197 191 198 192 total = 0; 199 193 last = queue_stats[0]; ··· 234 234 235 235 nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num); 236 236 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; 238 239 mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(flags)); 239 240 mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(flags)); 240 241 ··· 254 253 } 255 254 256 255 return nl; 256 + } 257 + 258 + static void sleep_ms(uint32_t delay) 259 + { 260 + struct timespec ts = { .tv_sec = delay / 1000 }; 261 + 262 + delay %= 1000; 263 + 264 + ts.tv_nsec = delay * 1000llu * 1000llu; 265 + 266 + nanosleep(&ts, NULL); 257 267 } 258 268 259 269 static int mainloop(void) ··· 290 278 291 279 ret = mnl_socket_recvfrom(nl, buf, buflen); 292 280 if (ret == -1) { 293 - if (errno == ENOBUFS) 281 + if (errno == ENOBUFS || errno == EINTR) 294 282 continue; 295 283 296 284 if (errno == EAGAIN) { ··· 310 298 } 311 299 312 300 id = ret - MNL_CB_OK; 313 - nlh = nfq_build_verdict(buf, id, opts.queue_num, NF_ACCEPT); 301 + if (opts.delay_ms) 302 + sleep_ms(opts.delay_ms); 303 + 304 + nlh = nfq_build_verdict(buf, id, opts.queue_num, opts.verdict); 314 305 if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { 315 306 perror("mnl_socket_sendto"); 316 307 exit(EXIT_FAILURE); ··· 329 314 { 330 315 int c; 331 316 332 - while ((c = getopt(argc, argv, "chvt:q:")) != -1) { 317 + while ((c = getopt(argc, argv, "chvt:q:Q:d:G")) != -1) { 333 318 switch (c) { 334 319 case 'c': 335 320 opts.count_packets = true; ··· 343 328 if (opts.queue_num > 0xffff) 344 329 opts.queue_num = 0; 345 330 break; 331 + case 'Q': 332 + opts.verdict = atoi(optarg); 333 + if (opts.verdict > 0xffff) { 334 + fprintf(stderr, "Expected destination queue number\n"); 335 + exit(1); 336 + } 337 + 338 + opts.verdict <<= 16; 339 + opts.verdict |= NF_QUEUE; 340 + break; 341 + case 'd': 342 + opts.delay_ms = atoi(optarg); 343 + if (opts.delay_ms == 0) { 344 + fprintf(stderr, "Expected nonzero delay (in milliseconds)\n"); 345 + exit(1); 346 + } 347 + break; 346 348 case 't': 347 349 opts.timeout = atoi(optarg); 350 + break; 351 + case 'G': 352 + opts.gso_enabled = false; 348 353 break; 349 354 case 'v': 350 355 opts.verbose++; 351 356 break; 352 357 } 353 358 } 359 + 360 + if (opts.verdict != NF_ACCEPT && (opts.verdict >> 16 == opts.queue_num)) { 361 + fprintf(stderr, "Cannot use same destination and source queue\n"); 362 + exit(1); 363 + } 354 364 } 355 365 356 366 int main(int argc, char *argv[]) 357 367 { 358 368 int ret; 369 + 370 + opts.verdict = NF_ACCEPT; 371 + opts.gso_enabled = true; 359 372 360 373 parse_opts(argc, argv); 361 374
+57 -13
tools/testing/selftests/netfilter/nft_queue.sh
··· 12 12 ns1="ns1-$sfx" 13 13 ns2="ns2-$sfx" 14 14 nsrouter="nsrouter-$sfx" 15 + timeout=4 15 16 16 17 cleanup() 17 18 { ··· 21 20 ip netns del ${nsrouter} 22 21 rm -f "$TMPFILE0" 23 22 rm -f "$TMPFILE1" 23 + rm -f "$TMPFILE2" "$TMPFILE3" 24 24 } 25 25 26 26 nft --version > /dev/null 2>&1 ··· 44 42 45 43 TMPFILE0=$(mktemp) 46 44 TMPFILE1=$(mktemp) 45 + TMPFILE2=$(mktemp) 46 + TMPFILE3=$(mktemp) 47 47 trap cleanup EXIT 48 48 49 49 ip netns add ${ns1} ··· 87 83 local name=$1 88 84 local prio=$2 89 85 90 - ip netns exec ${nsrouter} nft -f - <<EOF 86 + ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 91 87 table inet $name { 92 88 chain nfq { 93 89 ip protocol icmp queue bypass ··· 122 118 load_counter_ruleset() { 123 119 local prio=$1 124 120 125 - ip netns exec ${nsrouter} nft -f - <<EOF 121 + ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 126 122 table inet countrules { 127 123 chain pre { 128 124 type filter hook prerouting priority $prio; policy accept; ··· 179 175 test_queue_blackhole() { 180 176 local proto=$1 181 177 182 - ip netns exec ${nsrouter} nft -f - <<EOF 178 + ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 183 179 table $proto blackh { 184 180 chain forward { 185 181 type filter hook forward priority 0; policy accept; ··· 188 184 } 189 185 EOF 190 186 if [ $proto = "ip" ] ;then 191 - ip netns exec ${ns1} ping -c 1 -q 10.0.2.99 > /dev/null 187 + ip netns exec ${ns1} ping -W 2 -c 1 -q 10.0.2.99 > /dev/null 192 188 lret=$? 193 189 elif [ $proto = "ip6" ]; then 194 - ip netns exec ${ns1} ping -c 1 -q dead:2::99 > /dev/null 190 + ip netns exec ${ns1} ping -W 2 -c 1 -q dead:2::99 > /dev/null 195 191 lret=$? 196 192 else 197 193 lret=111 ··· 218 214 local last="" 219 215 220 216 # spawn nf-queue listeners 221 - ip netns exec ${nsrouter} ./nf-queue -c -q 0 -t 3 > "$TMPFILE0" & 222 - ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t 3 > "$TMPFILE1" & 217 + ip netns exec ${nsrouter} ./nf-queue -c -q 0 -t $timeout > "$TMPFILE0" & 218 + ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE1" & 223 219 sleep 1 224 220 test_ping 225 221 ret=$? ··· 254 250 255 251 test_tcp_forward() 256 252 { 257 - ip netns exec ${nsrouter} ./nf-queue -q 2 -t 10 & 253 + ip netns exec ${nsrouter} ./nf-queue -q 2 -t $timeout & 258 254 local nfqpid=$! 259 255 260 256 tmpfile=$(mktemp) || exit 1 261 - dd conv=sparse status=none if=/dev/zero bs=1M count=100 of=$tmpfile 257 + dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 262 258 ip netns exec ${ns2} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 263 259 local rpid=$! 264 260 ··· 274 270 275 271 test_tcp_localhost() 276 272 { 277 - tc -net "${nsrouter}" qdisc add dev lo root netem loss random 1% 278 - 279 273 tmpfile=$(mktemp) || exit 1 280 274 281 - dd conv=sparse status=none if=/dev/zero bs=1M count=900 of=$tmpfile 275 + dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 282 276 ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 283 277 local rpid=$! 284 278 285 - ip netns exec ${nsrouter} ./nf-queue -q 3 -t 30 & 279 + ip netns exec ${nsrouter} ./nf-queue -q 3 -t $timeout & 286 280 local nfqpid=$! 287 281 288 282 sleep 1 ··· 289 287 290 288 wait $rpid 291 289 [ $? -eq 0 ] && echo "PASS: tcp via loopback" 290 + wait 2>/dev/null 291 + } 292 + 293 + test_tcp_localhost_requeue() 294 + { 295 + ip netns exec ${nsrouter} nft -f /dev/stdin <<EOF 296 + flush ruleset 297 + table inet filter { 298 + chain output { 299 + type filter hook output priority 0; policy accept; 300 + tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0 301 + } 302 + chain post { 303 + type filter hook postrouting priority 0; policy accept; 304 + tcp dport 12345 limit rate 1/second burst 1 packets counter queue num 0 305 + } 306 + } 307 + EOF 308 + tmpfile=$(mktemp) || exit 1 309 + dd conv=sparse status=none if=/dev/zero bs=1M count=200 of=$tmpfile 310 + ip netns exec ${nsrouter} nc -w 5 -l -p 12345 <"$tmpfile" >/dev/null & 311 + local rpid=$! 312 + 313 + ip netns exec ${nsrouter} ./nf-queue -c -q 1 -t $timeout > "$TMPFILE2" & 314 + 315 + # nfqueue 1 will be called via output hook. But this time, 316 + # re-queue the packet to nfqueue program on queue 2. 317 + ip netns exec ${nsrouter} ./nf-queue -G -d 150 -c -q 0 -Q 1 -t $timeout > "$TMPFILE3" & 318 + 319 + sleep 1 320 + ip netns exec ${nsrouter} nc -w 5 127.0.0.1 12345 <"$tmpfile" > /dev/null 321 + rm -f "$tmpfile" 322 + 323 + wait 324 + 325 + if ! diff -u "$TMPFILE2" "$TMPFILE3" ; then 326 + echo "FAIL: lost packets during requeue?!" 1>&2 327 + return 328 + fi 329 + 330 + echo "PASS: tcp via loopback and re-queueing" 292 331 } 293 332 294 333 ip netns exec ${nsrouter} sysctl net.ipv6.conf.all.forwarding=1 > /dev/null ··· 371 328 372 329 test_tcp_forward 373 330 test_tcp_localhost 331 + test_tcp_localhost_requeue 374 332 375 333 exit $ret