@@ -510,15 +510,15 @@ EOF
510510
511511udp_listener_ready ()
512512{
513- ss -S -N " $1 " -uln -o " sport = :12345 " | grep -q 12345
513+ ss -S -N " $1 " -uln -o " sport = :$2 " | grep -q " $2 "
514514}
515515
516516output_files_written ()
517517{
518518 test -s " $1 " && test -s " $2 "
519519}
520520
521- test_udp_ct_race ()
521+ test_udp_nat_race ()
522522{
523523 ip netns exec " $nsrouter " nft -f /dev/stdin << EOF
524524flush ruleset
545545 ip netns exec " $nsrouter " ./nf_queue -q 12 -d 1000 &
546546 local nfqpid=$!
547547
548- busywait " $BUSYWAIT_TIMEOUT " udp_listener_ready " $ns2 "
549- busywait " $BUSYWAIT_TIMEOUT " udp_listener_ready " $ns3 "
548+ busywait " $BUSYWAIT_TIMEOUT " udp_listener_ready " $ns2 " 12345
549+ busywait " $BUSYWAIT_TIMEOUT " udp_listener_ready " $ns3 " 12345
550550 busywait " $BUSYWAIT_TIMEOUT " nf_queue_wait " $nsrouter " 12
551551
552552 # Send two packets, one should end up in ns1, other in ns2.
557557
558558 busywait 10000 output_files_written " $TMPFILE1 " " $TMPFILE2 "
559559
560- kill " $nfqpid "
560+ kill " $nfqpid " " $rpid1 " " $rpid2 "
561561
562562 if ! ip netns exec " $nsrouter " bash -c ' conntrack -L -p udp --dport 12345 2>/dev/null | wc -l | grep -q "^1"' ; then
563563 echo " FAIL: Expected One udp conntrack entry"
@@ -585,6 +585,135 @@ EOF
585585 echo " PASS: both udp receivers got one packet each"
586586}
587587
588+ # Make sure UDPGRO aggregated packets don't lose
589+ # their skb->nfct entry when nfqueue passes the
590+ # skb to userspace with software gso segmentation on.
591+ test_udp_gro_ct ()
592+ {
593+ local errprefix=" FAIL: test_udp_gro_ct:"
594+
595+ ip netns exec " $nsrouter " conntrack -F 2> /dev/null
596+
597+ ip netns exec " $nsrouter " nft -f /dev/stdin << EOF
598+ flush ruleset
599+ table inet udpq {
600+ # Number of packets/bytes queued to userspace
601+ counter toqueue { }
602+ # Number of packets/bytes reinjected from userspace with 'ct new' intact
603+ counter fromqueue { }
604+ # These two counters should be identical and not 0.
605+
606+ chain prerouting {
607+ type filter hook prerouting priority -300; policy accept;
608+
609+ # userspace sends small packets, if < 1000, UDPGRO did
610+ # not kick in, but test needs a 'new' conntrack with udpgro skb.
611+ meta iifname veth0 meta l4proto udp meta length > 1000 accept
612+
613+ # don't pick up non-gso packets and don't queue them to
614+ # userspace.
615+ notrack
616+ }
617+
618+ chain postrouting {
619+ type filter hook postrouting priority 0; policy accept;
620+
621+ # Only queue unconfirmed fraglist gro skbs to userspace.
622+ udp dport 12346 ct status ! confirmed counter name "toqueue" mark set 1 queue num 1
623+ }
624+
625+ chain validate {
626+ type filter hook postrouting priority 1; policy accept;
627+ # ... and only count those that were reinjected with the
628+ # skb->nfct intact.
629+ mark 1 counter name "fromqueue"
630+ }
631+ }
632+ EOF
633+ timeout 10 ip netns exec " $ns2 " socat UDP-LISTEN:12346,fork,pf=ipv4 OPEN:" $TMPFILE1 " ,trunc &
634+ local rpid=$!
635+
636+ ip netns exec " $nsrouter " ./nf_queue -G -c -q 1 -t 2 > " $TMPFILE2 " &
637+ local nfqpid=$!
638+
639+ ip netns exec " $nsrouter " ethtool -K " veth0" rx-udp-gro-forwarding on rx-gro-list on generic-receive-offload on
640+
641+ busywait " $BUSYWAIT_TIMEOUT " udp_listener_ready " $ns2 " 12346
642+ busywait " $BUSYWAIT_TIMEOUT " nf_queue_wait " $nsrouter " 1
643+
644+ local bs=512
645+ local count=$(( (32 * 1024 * 1024 ) / bs))
646+ dd if=/dev/zero bs=" $bs " count=" $count " 2> /dev/null | for i in $( seq 1 16) ; do
647+ timeout 5 ip netns exec " $ns1 " \
648+ socat -u -b 512 STDIN UDP-DATAGRAM:10.0.2.99:12346,reuseport,bind=0.0.0.0:55221 &
649+ done
650+
651+ busywait 10000 test -s " $TMPFILE1 "
652+
653+ kill " $rpid "
654+
655+ wait
656+
657+ local p
658+ local b
659+ local pqueued
660+ local bqueued
661+
662+ c=$( ip netns exec " $nsrouter " nft list counter inet udpq " toqueue" | grep packets)
663+ read p pqueued b bqueued << EOF
664+ $c
665+ EOF
666+ local preinject
667+ local breinject
668+ c=$( ip netns exec " $nsrouter " nft list counter inet udpq " fromqueue" | grep packets)
669+ read p preinject b breinject << EOF
670+ $c
671+ EOF
672+ ip netns exec " $nsrouter " ethtool -K " veth0" rx-udp-gro-forwarding off
673+ ip netns exec " $nsrouter " ethtool -K " veth1" rx-udp-gro-forwarding off
674+
675+ if [ " $pqueued " -eq 0 ]; then
676+ # happens when gro did not build at least on aggregate
677+ echo " SKIP: No packets were queued"
678+ return
679+ fi
680+
681+ local saw_ct_entry=0
682+ if ip netns exec " $nsrouter " bash -c ' conntrack -L -p udp --dport 12346 2>/dev/null | wc -l | grep -q "^1"' ; then
683+ saw_ct_entry=1
684+ else
685+ echo " $errprefix Expected udp conntrack entry"
686+ ip netns exec " $nsrouter " conntrack -L
687+ ret=1
688+ fi
689+
690+ if [ " $pqueued " -ge " $preinject " ] ; then
691+ echo " $errprefix Expected software segmentation to occur, had $pqueued and $preinject "
692+ ret=1
693+ return
694+ fi
695+
696+ # sw segmentation adds extra udp and ip headers.
697+ local breinject_expect=$(( preinject * (512 + 20 + 8 )) )
698+
699+ if [ " $breinject " -eq " $breinject_expect " ]; then
700+ if [ " $saw_ct_entry " -eq 1 ]; then
701+ echo " PASS: fraglist gro skb passed with conntrack entry"
702+ else
703+ echo " $errprefix fraglist gro skb passed without conntrack entry"
704+ ret=1
705+ fi
706+ else
707+ echo " $errprefix Counter mismatch, conntrack entry dropped by nfqueue? Queued: $pqueued , $bqueued . Post-queue: $preinject , $breinject . Expected $breinject_expect "
708+ ret=1
709+ fi
710+
711+ if ! ip netns exec " $nsrouter " nft delete table inet udpq; then
712+ echo " $errprefix : Could not delete udpq table"
713+ ret=1
714+ fi
715+ }
716+
588717test_queue_removal ()
589718{
590719 read tainted_then < /proc/sys/kernel/tainted
@@ -663,7 +792,8 @@ test_tcp_localhost_connectclose
663792test_tcp_localhost_requeue
664793test_sctp_forward
665794test_sctp_output
666- test_udp_ct_race
795+ test_udp_nat_race
796+ test_udp_gro_ct
667797
668798# should be last, adds vrf device in ns1 and changes routes
669799test_icmp_vrf
0 commit comments