Skip to content

Commit dc42504

Browse files
committed
Merge: [9.6 P2] IPv6: stable backport from upstream
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/6127 JIRA: https://issues.redhat.com/browse/RHEL-73281 Signed-off-by: Hangbin Liu <haliu@redhat.com> Approved-by: Florian Westphal <fwestpha@redhat.com> Approved-by: Marcelo Ricardo Leitner <mleitner@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: Rado Vrbovsky <rvrbovsk@redhat.com>
2 parents 01f09ef + 4dc1ebd commit dc42504

File tree

9 files changed

+410
-33
lines changed

9 files changed

+410
-33
lines changed

include/net/ip6_route.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,18 +132,26 @@ void rt6_age_exceptions(struct fib6_info *f6i, struct fib6_gc_args *gc_args,
132132

133133
static inline int ip6_route_get_saddr(struct net *net, struct fib6_info *f6i,
134134
const struct in6_addr *daddr,
135-
unsigned int prefs,
135+
unsigned int prefs, int l3mdev_index,
136136
struct in6_addr *saddr)
137137
{
138+
struct net_device *l3mdev;
139+
struct net_device *dev;
140+
bool same_vrf;
138141
int err = 0;
139142

140-
if (f6i && f6i->fib6_prefsrc.plen) {
143+
rcu_read_lock();
144+
145+
l3mdev = dev_get_by_index_rcu(net, l3mdev_index);
146+
if (!f6i || !f6i->fib6_prefsrc.plen || l3mdev)
147+
dev = f6i ? fib6_info_nh_dev(f6i) : NULL;
148+
same_vrf = !l3mdev || l3mdev_master_dev_rcu(dev) == l3mdev;
149+
if (f6i && f6i->fib6_prefsrc.plen && same_vrf)
141150
*saddr = f6i->fib6_prefsrc.addr;
142-
} else {
143-
struct net_device *dev = f6i ? fib6_info_nh_dev(f6i) : NULL;
151+
else
152+
err = ipv6_dev_get_saddr(net, same_vrf ? dev : l3mdev, daddr, prefs, saddr);
144153

145-
err = ipv6_dev_get_saddr(net, dev, daddr, prefs, saddr);
146-
}
154+
rcu_read_unlock();
147155

148156
return err;
149157
}

net/ipv6/addrconf.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1823,7 +1823,8 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
18231823
master, &dst,
18241824
scores, hiscore_idx);
18251825

1826-
if (scores[hiscore_idx].ifa)
1826+
if (scores[hiscore_idx].ifa &&
1827+
scores[hiscore_idx].scopedist >= 0)
18271828
goto out;
18281829
}
18291830

net/ipv6/ip6_fib.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,8 +1183,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
11831183
while (sibling) {
11841184
if (sibling->fib6_metric == rt->fib6_metric &&
11851185
rt6_qualify_for_ecmp(sibling)) {
1186-
list_add_tail(&rt->fib6_siblings,
1187-
&sibling->fib6_siblings);
1186+
list_add_tail_rcu(&rt->fib6_siblings,
1187+
&sibling->fib6_siblings);
11881188
break;
11891189
}
11901190
sibling = rcu_dereference_protected(sibling->fib6_next,
@@ -1245,7 +1245,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
12451245
fib6_siblings)
12461246
sibling->fib6_nsiblings--;
12471247
rt->fib6_nsiblings = 0;
1248-
list_del_init(&rt->fib6_siblings);
1248+
list_del_rcu(&rt->fib6_siblings);
12491249
rt6_multipath_rebalance(next_sibling);
12501250
return err;
12511251
}
@@ -1959,7 +1959,7 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
19591959
&rt->fib6_siblings, fib6_siblings)
19601960
sibling->fib6_nsiblings--;
19611961
rt->fib6_nsiblings = 0;
1962-
list_del_init(&rt->fib6_siblings);
1962+
list_del_rcu(&rt->fib6_siblings);
19631963
rt6_multipath_rebalance(next_sibling);
19641964
}
19651965

net/ipv6/ip6_output.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,
11301130
from = rt ? rcu_dereference(rt->from) : NULL;
11311131
err = ip6_route_get_saddr(net, from, &fl6->daddr,
11321132
sk ? inet6_sk(sk)->srcprefs : 0,
1133+
fl6->flowi6_l3mdev,
11331134
&fl6->saddr);
11341135
rcu_read_unlock();
11351136

net/ipv6/route.c

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
377377
{
378378
struct rt6_info *rt = (struct rt6_info *)dst;
379379
struct inet6_dev *idev = rt->rt6i_idev;
380+
struct fib6_info *from;
380381

381382
if (idev && idev->dev != blackhole_netdev) {
382383
struct inet6_dev *blackhole_idev = in6_dev_get(blackhole_netdev);
@@ -386,6 +387,8 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
386387
in6_dev_put(idev);
387388
}
388389
}
390+
from = unrcu_pointer(xchg(&rt->from, NULL));
391+
fib6_info_release(from);
389392
}
390393

391394
static bool __rt6_check_expired(const struct rt6_info *rt)
@@ -416,8 +419,8 @@ void fib6_select_path(const struct net *net, struct fib6_result *res,
416419
struct flowi6 *fl6, int oif, bool have_oif_match,
417420
const struct sk_buff *skb, int strict)
418421
{
419-
struct fib6_info *sibling, *next_sibling;
420422
struct fib6_info *match = res->f6i;
423+
struct fib6_info *sibling;
421424

422425
if (!match->nh && (!match->fib6_nsiblings || have_oif_match))
423426
goto out;
@@ -443,8 +446,8 @@ void fib6_select_path(const struct net *net, struct fib6_result *res,
443446
if (fl6->mp_hash <= atomic_read(&match->fib6_nh->fib_nh_upper_bound))
444447
goto out;
445448

446-
list_for_each_entry_safe(sibling, next_sibling, &match->fib6_siblings,
447-
fib6_siblings) {
449+
list_for_each_entry_rcu(sibling, &match->fib6_siblings,
450+
fib6_siblings) {
448451
const struct fib6_nh *nh = sibling->fib6_nh;
449452
int nh_upper_bound;
450453

@@ -1449,7 +1452,6 @@ static DEFINE_SPINLOCK(rt6_exception_lock);
14491452
static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
14501453
struct rt6_exception *rt6_ex)
14511454
{
1452-
struct fib6_info *from;
14531455
struct net *net;
14541456

14551457
if (!bucket || !rt6_ex)
@@ -1461,8 +1463,6 @@ static void rt6_remove_exception(struct rt6_exception_bucket *bucket,
14611463
/* purge completely the exception to allow releasing the held resources:
14621464
* some [sk] cache may keep the dst around for unlimited time
14631465
*/
1464-
from = xchg((__force struct fib6_info **)&rt6_ex->rt6i->from, NULL);
1465-
fib6_info_release(from);
14661466
dst_dev_put(&rt6_ex->rt6i->dst);
14671467

14681468
hlist_del_rcu(&rt6_ex->hlist);
@@ -5216,14 +5216,18 @@ static void ip6_route_mpath_notify(struct fib6_info *rt,
52165216
* nexthop. Since sibling routes are always added at the end of
52175217
* the list, find the first sibling of the last route appended
52185218
*/
5219+
rcu_read_lock();
5220+
52195221
if ((nlflags & NLM_F_APPEND) && rt_last && rt_last->fib6_nsiblings) {
5220-
rt = list_first_entry(&rt_last->fib6_siblings,
5221-
struct fib6_info,
5222-
fib6_siblings);
5222+
rt = list_first_or_null_rcu(&rt_last->fib6_siblings,
5223+
struct fib6_info,
5224+
fib6_siblings);
52235225
}
52245226

52255227
if (rt)
52265228
inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
5229+
5230+
rcu_read_unlock();
52275231
}
52285232

52295233
static bool ip6_route_mpath_should_notify(const struct fib6_info *rt)
@@ -5568,17 +5572,21 @@ static size_t rt6_nlmsg_size(struct fib6_info *f6i)
55685572
nexthop_for_each_fib6_nh(f6i->nh, rt6_nh_nlmsg_size,
55695573
&nexthop_len);
55705574
} else {
5571-
struct fib6_info *sibling, *next_sibling;
55725575
struct fib6_nh *nh = f6i->fib6_nh;
5576+
struct fib6_info *sibling;
55735577

55745578
nexthop_len = 0;
55755579
if (f6i->fib6_nsiblings) {
55765580
rt6_nh_nlmsg_size(nh, &nexthop_len);
55775581

5578-
list_for_each_entry_safe(sibling, next_sibling,
5579-
&f6i->fib6_siblings, fib6_siblings) {
5582+
rcu_read_lock();
5583+
5584+
list_for_each_entry_rcu(sibling, &f6i->fib6_siblings,
5585+
fib6_siblings) {
55805586
rt6_nh_nlmsg_size(sibling->fib6_nh, &nexthop_len);
55815587
}
5588+
5589+
rcu_read_unlock();
55825590
}
55835591
nexthop_len += lwtunnel_get_encap_size(nh->fib_nh_lws);
55845592
}
@@ -5708,7 +5716,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
57085716
goto nla_put_failure;
57095717
} else if (dest) {
57105718
struct in6_addr saddr_buf;
5711-
if (ip6_route_get_saddr(net, rt, dest, 0, &saddr_buf) == 0 &&
5719+
if (ip6_route_get_saddr(net, rt, dest, 0, 0, &saddr_buf) == 0 &&
57125720
nla_put_in6_addr(skb, RTA_PREFSRC, &saddr_buf))
57135721
goto nla_put_failure;
57145722
}
@@ -5742,7 +5750,7 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
57425750
lwtunnel_fill_encap(skb, dst->lwtstate, RTA_ENCAP, RTA_ENCAP_TYPE) < 0)
57435751
goto nla_put_failure;
57445752
} else if (rt->fib6_nsiblings) {
5745-
struct fib6_info *sibling, *next_sibling;
5753+
struct fib6_info *sibling;
57465754
struct nlattr *mp;
57475755

57485756
mp = nla_nest_start_noflag(skb, RTA_MULTIPATH);
@@ -5754,14 +5762,21 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
57545762
0) < 0)
57555763
goto nla_put_failure;
57565764

5757-
list_for_each_entry_safe(sibling, next_sibling,
5758-
&rt->fib6_siblings, fib6_siblings) {
5765+
rcu_read_lock();
5766+
5767+
list_for_each_entry_rcu(sibling, &rt->fib6_siblings,
5768+
fib6_siblings) {
57595769
if (fib_add_nexthop(skb, &sibling->fib6_nh->nh_common,
57605770
sibling->fib6_nh->fib_nh_weight,
5761-
AF_INET6, 0) < 0)
5771+
AF_INET6, 0) < 0) {
5772+
rcu_read_unlock();
5773+
57625774
goto nla_put_failure;
5775+
}
57635776
}
57645777

5778+
rcu_read_unlock();
5779+
57655780
nla_nest_end(skb, mp);
57665781
} else if (rt->nh) {
57675782
if (nla_put_u32(skb, RTA_NH_ID, rt->nh->id))
@@ -6198,7 +6213,7 @@ void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
61986213
err = -ENOBUFS;
61996214
seq = info->nlh ? info->nlh->nlmsg_seq : 0;
62006215

6201-
skb = nlmsg_new(rt6_nlmsg_size(rt), gfp_any());
6216+
skb = nlmsg_new(rt6_nlmsg_size(rt), GFP_ATOMIC);
62026217
if (!skb)
62036218
goto errout;
62046219

@@ -6211,7 +6226,7 @@ void inet6_rt_notify(int event, struct fib6_info *rt, struct nl_info *info,
62116226
goto errout;
62126227
}
62136228
rtnl_notify(skb, net, info->portid, RTNLGRP_IPV6_ROUTE,
6214-
info->nlh, gfp_any());
6229+
info->nlh, GFP_ATOMIC);
62156230
return;
62166231
errout:
62176232
if (err < 0)

tools/testing/selftests/net/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ TEST_PROGS += test_vxlan_mdb.sh
6464
TEST_PROGS += test_bridge_neigh_suppress.sh
6565
TEST_PROGS += test_vxlan_nolocalbypass.sh
6666
TEST_PROGS += test_bridge_backup_port.sh
67+
TEST_PROGS += ipv6_route_update_soft_lockup.sh
6768

6869
TEST_FILES := settings
6970
TEST_FILES += in_netns.sh lib.sh net_helper.sh setup_loopback.sh setup_veth.sh

0 commit comments

Comments
 (0)