Skip to content

Commit fa17a6d

Browse files
edumazetPaolo Abeni
authored andcommitted
ipv6: lockless IPV6_ADDR_PREFERENCES implementation
We have data-races while reading np->srcprefs Switch the field to a plain byte, add READ_ONCE() and WRITE_ONCE() annotations where needed, and IPV6_ADDR_PREFERENCES setsockopt() can now be lockless. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: David Ahern <dsahern@kernel.org> Link: https://lore.kernel.org/r/20230918142321.1794107-1-edumazet@google.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
1 parent 6a23c55 commit fa17a6d

6 files changed

Lines changed: 22 additions & 28 deletions

File tree

include/linux/ipv6.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ struct ipv6_pinfo {
243243
} rxopt;
244244

245245
/* sockopt flags */
246-
__u8 srcprefs:3; /* 001: prefer temporary address
246+
__u8 srcprefs; /* 001: prefer temporary address
247247
* 010: prefer public address
248248
* 100: prefer care-of address
249249
*/

include/net/ip6_route.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,12 @@ struct route_info {
5353
*/
5454
static inline int rt6_srcprefs2flags(unsigned int srcprefs)
5555
{
56-
/* No need to bitmask because srcprefs have only 3 bits. */
57-
return srcprefs << 3;
56+
return (srcprefs & IPV6_PREFER_SRC_MASK) << 3;
5857
}
5958

6059
static inline unsigned int rt6_flags2srcprefs(int flags)
6160
{
62-
return (flags >> 3) & 7;
61+
return (flags >> 3) & IPV6_PREFER_SRC_MASK;
6362
}
6463

6564
static inline bool rt6_need_strict(const struct in6_addr *daddr)

include/net/ipv6.h

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,10 +1306,13 @@ static inline void ip6_sock_set_recverr(struct sock *sk)
13061306
inet6_set_bit(RECVERR6, sk);
13071307
}
13081308

1309-
static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val)
1309+
#define IPV6_PREFER_SRC_MASK (IPV6_PREFER_SRC_TMP | IPV6_PREFER_SRC_PUBLIC | \
1310+
IPV6_PREFER_SRC_COA)
1311+
1312+
static inline int ip6_sock_set_addr_preferences(struct sock *sk, int val)
13101313
{
1314+
unsigned int prefmask = ~IPV6_PREFER_SRC_MASK;
13111315
unsigned int pref = 0;
1312-
unsigned int prefmask = ~0;
13131316

13141317
/* check PUBLIC/TMP/PUBTMP_DEFAULT conflicts */
13151318
switch (val & (IPV6_PREFER_SRC_PUBLIC |
@@ -1359,20 +1362,11 @@ static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val)
13591362
return -EINVAL;
13601363
}
13611364

1362-
inet6_sk(sk)->srcprefs = (inet6_sk(sk)->srcprefs & prefmask) | pref;
1365+
WRITE_ONCE(inet6_sk(sk)->srcprefs,
1366+
(READ_ONCE(inet6_sk(sk)->srcprefs) & prefmask) | pref);
13631367
return 0;
13641368
}
13651369

1366-
static inline int ip6_sock_set_addr_preferences(struct sock *sk, int val)
1367-
{
1368-
int ret;
1369-
1370-
lock_sock(sk);
1371-
ret = __ip6_sock_set_addr_preferences(sk, val);
1372-
release_sock(sk);
1373-
return ret;
1374-
}
1375-
13761370
static inline void ip6_sock_set_recvpktinfo(struct sock *sk)
13771371
{
13781372
lock_sock(sk);

net/ipv6/ip6_output.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ static int ip6_dst_lookup_tail(struct net *net, const struct sock *sk,
11131113
rcu_read_lock();
11141114
from = rt ? rcu_dereference(rt->from) : NULL;
11151115
err = ip6_route_get_saddr(net, from, &fl6->daddr,
1116-
sk ? inet6_sk(sk)->srcprefs : 0,
1116+
sk ? READ_ONCE(inet6_sk(sk)->srcprefs) : 0,
11171117
&fl6->saddr);
11181118
rcu_read_unlock();
11191119

net/ipv6/ipv6_sockglue.c

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,10 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
505505
return -EINVAL;
506506
inet6_assign_bit(SNDFLOW, sk, valbool);
507507
return 0;
508+
case IPV6_ADDR_PREFERENCES:
509+
if (optlen < sizeof(int))
510+
return -EINVAL;
511+
return ip6_sock_set_addr_preferences(sk, val);
508512
}
509513
if (needs_rtnl)
510514
rtnl_lock();
@@ -964,11 +968,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
964968
retv = xfrm_user_policy(sk, optname, optval, optlen);
965969
break;
966970

967-
case IPV6_ADDR_PREFERENCES:
968-
if (optlen < sizeof(int))
969-
goto e_inval;
970-
retv = __ip6_sock_set_addr_preferences(sk, val);
971-
break;
972971
case IPV6_RECVFRAGSIZE:
973972
np->rxopt.bits.recvfragsize = valbool;
974973
retv = 0;
@@ -1415,23 +1414,25 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
14151414
}
14161415

14171416
case IPV6_ADDR_PREFERENCES:
1417+
{
1418+
u8 srcprefs = READ_ONCE(np->srcprefs);
14181419
val = 0;
14191420

1420-
if (np->srcprefs & IPV6_PREFER_SRC_TMP)
1421+
if (srcprefs & IPV6_PREFER_SRC_TMP)
14211422
val |= IPV6_PREFER_SRC_TMP;
1422-
else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC)
1423+
else if (srcprefs & IPV6_PREFER_SRC_PUBLIC)
14231424
val |= IPV6_PREFER_SRC_PUBLIC;
14241425
else {
14251426
/* XXX: should we return system default? */
14261427
val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT;
14271428
}
14281429

1429-
if (np->srcprefs & IPV6_PREFER_SRC_COA)
1430+
if (srcprefs & IPV6_PREFER_SRC_COA)
14301431
val |= IPV6_PREFER_SRC_COA;
14311432
else
14321433
val |= IPV6_PREFER_SRC_HOME;
14331434
break;
1434-
1435+
}
14351436
case IPV6_MINHOPCOUNT:
14361437
val = READ_ONCE(np->min_hopcount);
14371438
break;

net/ipv6/route.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2622,7 +2622,7 @@ static struct dst_entry *ip6_route_output_flags_noref(struct net *net,
26222622
if (!any_src)
26232623
flags |= RT6_LOOKUP_F_HAS_SADDR;
26242624
else if (sk)
2625-
flags |= rt6_srcprefs2flags(inet6_sk(sk)->srcprefs);
2625+
flags |= rt6_srcprefs2flags(READ_ONCE(inet6_sk(sk)->srcprefs));
26262626

26272627
return fib6_rule_lookup(net, fl6, NULL, flags, ip6_pol_route_output);
26282628
}

0 commit comments

Comments
 (0)