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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'ipv6-fix-two-gc-issues-with-permanent-routes'

Kuniyuki Iwashima says:

====================
ipv6: Fix two GC issues with permanent routes.

Patch 1 fixes the unbounded growth of tb6_gc_hlist due to
permanent routes whose exception routes have all expired.

Patch 2 fixes an issue where exception routes tied to
permanent routes are not properly aged.

Patch 3 is a selftest for the issue fixed by Patch 2.
====================

Link: https://patch.msgid.link/20260320072317.2561779-1-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+94 -9
+20 -1
include/net/ip6_fib.h
··· 507 507 void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info, 508 508 unsigned int flags); 509 509 510 + void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args, 511 + unsigned long now); 510 512 void fib6_run_gc(unsigned long expires, struct net *net, bool force); 511 - 512 513 void fib6_gc_cleanup(void); 513 514 514 515 int fib6_init(void); 515 516 517 + #if IS_ENABLED(CONFIG_IPV6) 516 518 /* Add the route to the gc list if it is not already there 517 519 * 518 520 * The callers should hold f6i->fib6_table->tb6_lock. ··· 546 544 if (!hlist_unhashed(&f6i->gc_link)) 547 545 hlist_del_init(&f6i->gc_link); 548 546 } 547 + 548 + static inline void fib6_may_remove_gc_list(struct net *net, 549 + struct fib6_info *f6i) 550 + { 551 + struct fib6_gc_args gc_args; 552 + 553 + if (hlist_unhashed(&f6i->gc_link)) 554 + return; 555 + 556 + gc_args.timeout = READ_ONCE(net->ipv6.sysctl.ip6_rt_gc_interval); 557 + gc_args.more = 0; 558 + 559 + rcu_read_lock(); 560 + fib6_age_exceptions(f6i, &gc_args, jiffies); 561 + rcu_read_unlock(); 562 + } 563 + #endif 549 564 550 565 struct ipv6_route_iter { 551 566 struct seq_net_private p;
+2 -2
net/ipv6/addrconf.c
··· 2862 2862 fib6_add_gc_list(rt); 2863 2863 } else { 2864 2864 fib6_clean_expires(rt); 2865 - fib6_remove_gc_list(rt); 2865 + fib6_may_remove_gc_list(net, rt); 2866 2866 } 2867 2867 2868 2868 spin_unlock_bh(&table->tb6_lock); ··· 4840 4840 4841 4841 if (!(flags & RTF_EXPIRES)) { 4842 4842 fib6_clean_expires(f6i); 4843 - fib6_remove_gc_list(f6i); 4843 + fib6_may_remove_gc_list(net, f6i); 4844 4844 } else { 4845 4845 fib6_set_expires(f6i, expires); 4846 4846 fib6_add_gc_list(f6i);
+13 -2
net/ipv6/ip6_fib.c
··· 1133 1133 return -EEXIST; 1134 1134 if (!(rt->fib6_flags & RTF_EXPIRES)) { 1135 1135 fib6_clean_expires(iter); 1136 - fib6_remove_gc_list(iter); 1136 + fib6_may_remove_gc_list(info->nl_net, iter); 1137 1137 } else { 1138 1138 fib6_set_expires(iter, rt->expires); 1139 1139 fib6_add_gc_list(iter); ··· 2348 2348 /* 2349 2349 * Garbage collection 2350 2350 */ 2351 + void fib6_age_exceptions(struct fib6_info *rt, struct fib6_gc_args *gc_args, 2352 + unsigned long now) 2353 + { 2354 + bool may_expire = rt->fib6_flags & RTF_EXPIRES && rt->expires; 2355 + int old_more = gc_args->more; 2356 + 2357 + rt6_age_exceptions(rt, gc_args, now); 2358 + 2359 + if (!may_expire && old_more == gc_args->more) 2360 + fib6_remove_gc_list(rt); 2361 + } 2351 2362 2352 2363 static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args) 2353 2364 { ··· 2381 2370 * Note, that clones are aged out 2382 2371 * only if they are not in use now. 2383 2372 */ 2384 - rt6_age_exceptions(rt, gc_args, now); 2373 + fib6_age_exceptions(rt, gc_args, now); 2385 2374 2386 2375 return 0; 2387 2376 }
+1 -1
net/ipv6/route.c
··· 1033 1033 1034 1034 if (!addrconf_finite_timeout(lifetime)) { 1035 1035 fib6_clean_expires(rt); 1036 - fib6_remove_gc_list(rt); 1036 + fib6_may_remove_gc_list(net, rt); 1037 1037 } else { 1038 1038 fib6_set_expires(rt, jiffies + HZ * lifetime); 1039 1039 fib6_add_gc_list(rt);
+58 -3
tools/testing/selftests/net/fib_tests.sh
··· 868 868 check_rt_num 5 $($IP -6 route list |grep -v expires|grep 2001:20::|wc -l) 869 869 log_test $ret 0 "ipv6 route garbage collection (replace with permanent)" 870 870 871 + # Delete dummy_10 and remove all routes 872 + $IP link del dev dummy_10 873 + 874 + # rd6 is required for the next test. (ipv6toolkit) 875 + if [ ! -x "$(command -v rd6)" ]; then 876 + echo "SKIP: rd6 not found." 877 + set +e 878 + cleanup &> /dev/null 879 + return 880 + fi 881 + 882 + setup_ns ns2 883 + $IP link add veth1 type veth peer veth2 netns $ns2 884 + $IP link set veth1 up 885 + ip -netns $ns2 link set veth2 up 886 + $IP addr add fe80:dead::1/64 dev veth1 887 + ip -netns $ns2 addr add fe80:dead::2/64 dev veth2 888 + 889 + # Add NTF_ROUTER neighbour to prevent rt6_age_examine_exception() 890 + # from removing not-yet-expired exceptions. 891 + ip -netns $ns2 link set veth2 address 00:11:22:33:44:55 892 + $IP neigh add fe80:dead::3 lladdr 00:11:22:33:44:55 dev veth1 router 893 + 894 + $NS_EXEC sysctl -wq net.ipv6.conf.veth1.accept_redirects=1 895 + $NS_EXEC sysctl -wq net.ipv6.conf.veth1.forwarding=0 896 + 897 + # Temporary routes 898 + for i in $(seq 1 5); do 899 + # Expire route after $EXPIRE seconds 900 + $IP -6 route add 2001:10::$i \ 901 + via fe80:dead::2 dev veth1 expires $EXPIRE 902 + 903 + ip netns exec $ns2 rd6 -i veth2 \ 904 + -s fe80:dead::2 -d fe80:dead::1 \ 905 + -r 2001:10::$i -t fe80:dead::3 -p ICMP6 906 + done 907 + 908 + check_rt_num 5 $($IP -6 route list | grep expires | grep 2001:10:: | wc -l) 909 + 910 + # Promote to permanent routes by "prepend" (w/o NLM_F_EXCL and NLM_F_REPLACE) 911 + for i in $(seq 1 5); do 912 + # -EEXIST, but the temporary route becomes the permanent route. 913 + $IP -6 route append 2001:10::$i \ 914 + via fe80:dead::2 dev veth1 2>/dev/null || true 915 + done 916 + 917 + check_rt_num 5 $($IP -6 route list | grep -v expires | grep 2001:10:: | wc -l) 918 + check_rt_num 5 $($IP -6 route list cache | grep 2001:10:: | wc -l) 919 + 920 + # Trigger GC instead of waiting $GC_WAIT_TIME. 921 + # rt6_nh_dump_exceptions() just skips expired exceptions. 922 + $NS_EXEC sysctl -wq net.ipv6.route.flush=1 923 + check_rt_num 0 $($IP -6 route list cache | grep 2001:10:: | wc -l) 924 + log_test $ret 0 "ipv6 route garbage collection (promote to permanent routes)" 925 + 926 + $IP neigh del fe80:dead::3 lladdr 00:11:22:33:44:55 dev veth1 router 927 + $IP link del veth1 928 + 871 929 # ra6 is required for the next test. (ipv6toolkit) 872 930 if [ ! -x "$(command -v ra6)" ]; then 873 931 echo "SKIP: ra6 not found." ··· 933 875 cleanup &> /dev/null 934 876 return 935 877 fi 936 - 937 - # Delete dummy_10 and remove all routes 938 - $IP link del dev dummy_10 939 878 940 879 # Create a pair of veth devices to send a RA message from one 941 880 # device to another.