Skip to content

Commit 927a8ce

Browse files
pshelarBrian Maly
authored andcommitted
vxlan: avoid using stale vxlan socket.
When vxlan device is closed vxlan socket is freed. This operation can race with vxlan-xmit function which dereferences vxlan socket. Following patch uses RCU mechanism to avoid this situation. Orabug: 29927196 Reviewed-by: Rama Nichanamatlu <[email protected]> Signed-off-by: Pravin B Shelar <[email protected]> Signed-off-by: David S. Miller <[email protected]> (cherry picked from commit c6fcc4f) Signed-off-by: Venkat Venkatsubra <[email protected]> Signed-off-by: Brian Maly <[email protected]> Conflicts: drivers/net/vxlan.c Signed-off-by: Brian Maly <[email protected]>
1 parent 412851b commit 927a8ce

File tree

2 files changed

+45
-28
lines changed

2 files changed

+45
-28
lines changed

drivers/net/vxlan.c

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -988,28 +988,33 @@ static bool vxlan_snoop(struct net_device *dev,
988988
static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev)
989989
{
990990
struct vxlan_dev *vxlan;
991+
struct vxlan_sock *sock4;
992+
struct vxlan_sock *sock6 = NULL;
991993
unsigned short family = dev->default_dst.remote_ip.sa.sa_family;
992994

995+
sock4 = rtnl_dereference(dev->vn4_sock);
996+
993997
/* The vxlan_sock is only used by dev, leaving group has
994998
* no effect on other vxlan devices.
995999
*/
996-
if (family == AF_INET && dev->vn4_sock &&
997-
atomic_read(&dev->vn4_sock->refcnt) == 1)
1000+
if (family == AF_INET && sock4 && atomic_read(&sock4->refcnt) == 1)
9981001
return false;
9991002
#if IS_ENABLED(CONFIG_IPV6)
1000-
if (family == AF_INET6 && dev->vn6_sock &&
1001-
atomic_read(&dev->vn6_sock->refcnt) == 1)
1003+
sock6 = rtnl_dereference(dev->vn6_sock);
1004+
if (family == AF_INET6 && sock6 && atomic_read(&sock6->refcnt) == 1)
10021005
return false;
10031006
#endif
10041007

10051008
list_for_each_entry(vxlan, &vn->vxlan_list, next) {
10061009
if (!netif_running(vxlan->dev) || vxlan == dev)
10071010
continue;
10081011

1009-
if (family == AF_INET && vxlan->vn4_sock != dev->vn4_sock)
1012+
if (family == AF_INET &&
1013+
rtnl_dereference(vxlan->vn4_sock) != sock4)
10101014
continue;
10111015
#if IS_ENABLED(CONFIG_IPV6)
1012-
if (family == AF_INET6 && vxlan->vn6_sock != dev->vn6_sock)
1016+
if (family == AF_INET6 &&
1017+
rtnl_dereference(vxlan->vn6_sock) != sock6)
10131018
continue;
10141019
#endif
10151020

@@ -1047,24 +1052,27 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
10471052

10481053
static void vxlan_sock_release(struct vxlan_dev *vxlan)
10491054
{
1050-
bool ipv4 = __vxlan_sock_release_prep(vxlan->vn4_sock);
1055+
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
10511056
#if IS_ENABLED(CONFIG_IPV6)
1052-
bool ipv6 = __vxlan_sock_release_prep(vxlan->vn6_sock);
1057+
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
1058+
1059+
rcu_assign_pointer(vxlan->vn6_sock, NULL);
10531060
#endif
10541061

1062+
rcu_assign_pointer(vxlan->vn4_sock, NULL);
10551063
synchronize_net();
10561064

10571065
vxlan_vs_del_dev(vxlan);
10581066

1059-
if (ipv4) {
1060-
udp_tunnel_sock_release(vxlan->vn4_sock->sock);
1061-
kfree(vxlan->vn4_sock);
1067+
if (__vxlan_sock_release_prep(sock4)) {
1068+
udp_tunnel_sock_release(sock4->sock);
1069+
kfree(sock4);
10621070
}
10631071

10641072
#if IS_ENABLED(CONFIG_IPV6)
1065-
if (ipv6) {
1066-
udp_tunnel_sock_release(vxlan->vn6_sock->sock);
1067-
kfree(vxlan->vn6_sock);
1073+
if (__vxlan_sock_release_prep(sock6)) {
1074+
udp_tunnel_sock_release(sock6->sock);
1075+
kfree(sock6);
10681076
}
10691077
#endif
10701078
}
@@ -1080,18 +1088,21 @@ static int vxlan_igmp_join(struct vxlan_dev *vxlan)
10801088
int ret = -EINVAL;
10811089

10821090
if (ip->sa.sa_family == AF_INET) {
1091+
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
10831092
struct ip_mreqn mreq = {
10841093
.imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
10851094
.imr_ifindex = ifindex,
10861095
};
10871096

1088-
sk = vxlan->vn4_sock->sock->sk;
1097+
sk = sock4->sock->sk;
10891098
lock_sock(sk);
10901099
ret = ip_mc_join_group(sk, &mreq);
10911100
release_sock(sk);
10921101
#if IS_ENABLED(CONFIG_IPV6)
10931102
} else {
1094-
sk = vxlan->vn6_sock->sock->sk;
1103+
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
1104+
1105+
sk = sock6->sock->sk;
10951106
lock_sock(sk);
10961107
ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex,
10971108
&ip->sin6.sin6_addr);
@@ -1111,18 +1122,21 @@ static int vxlan_igmp_leave(struct vxlan_dev *vxlan)
11111122
int ret = -EINVAL;
11121123

11131124
if (ip->sa.sa_family == AF_INET) {
1125+
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
11141126
struct ip_mreqn mreq = {
11151127
.imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
11161128
.imr_ifindex = ifindex,
11171129
};
11181130

1119-
sk = vxlan->vn4_sock->sock->sk;
1131+
sk = sock4->sock->sk;
11201132
lock_sock(sk);
11211133
ret = ip_mc_leave_group(sk, &mreq);
11221134
release_sock(sk);
11231135
#if IS_ENABLED(CONFIG_IPV6)
11241136
} else {
1125-
sk = vxlan->vn6_sock->sock->sk;
1137+
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
1138+
1139+
sk = sock6->sock->sk;
11261140
lock_sock(sk);
11271141
ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex,
11281142
&ip->sin6.sin6_addr);
@@ -1934,9 +1948,11 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
19341948
vxlan->cfg.port_max, true);
19351949

19361950
if (dst->sa.sa_family == AF_INET) {
1937-
if (!vxlan->vn4_sock)
1951+
struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
1952+
1953+
if (!sock4)
19381954
goto drop;
1939-
sk = vxlan->vn4_sock->sock->sk;
1955+
sk = sock4->sock->sk;
19401956
memset(&fl4, 0, sizeof(fl4));
19411957
fl4.flowi4_oif = rdst->remote_ifindex;
19421958
fl4.flowi4_tos = RT_TOS(tos);
@@ -1990,13 +2006,14 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
19902006
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
19912007
#if IS_ENABLED(CONFIG_IPV6)
19922008
} else {
2009+
struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
19932010
struct dst_entry *ndst;
19942011
struct flowi6 fl6;
19952012
u32 flags;
19962013

1997-
if (!vxlan->vn6_sock)
2014+
if (!sock6)
19982015
goto drop;
1999-
sk = vxlan->vn6_sock->sock->sk;
2016+
sk = sock6->sock->sk;
20002017

20012018
memset(&fl6, 0, sizeof(fl6));
20022019
fl6.flowi6_oif = rdst->remote_ifindex;
@@ -2587,10 +2604,10 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
25872604
return PTR_ERR(vs);
25882605
#if IS_ENABLED(CONFIG_IPV6)
25892606
if (ipv6)
2590-
vxlan->vn6_sock = vs;
2607+
rcu_assign_pointer(vxlan->vn6_sock, vs);
25912608
else
25922609
#endif
2593-
vxlan->vn4_sock = vs;
2610+
rcu_assign_pointer(vxlan->vn4_sock, vs);
25942611
vxlan_vs_add_dev(vs, vxlan);
25952612
return 0;
25962613
}
@@ -2601,9 +2618,9 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)
26012618
bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA;
26022619
int ret = 0;
26032620

2604-
vxlan->vn4_sock = NULL;
2621+
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
26052622
#if IS_ENABLED(CONFIG_IPV6)
2606-
vxlan->vn6_sock = NULL;
2623+
RCU_INIT_POINTER(vxlan->vn6_sock, NULL);
26072624
if (ipv6 || metadata)
26082625
ret = __vxlan_sock_add(vxlan, true);
26092626
#endif

include/net/vxlan.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,9 @@ struct vxlan_config {
148148
struct vxlan_dev {
149149
struct hlist_node hlist; /* vni hash table */
150150
struct list_head next; /* vxlan's per namespace list */
151-
struct vxlan_sock *vn4_sock; /* listening socket for IPv4 */
151+
struct vxlan_sock __rcu *vn4_sock; /* listening socket for IPv4 */
152152
#if IS_ENABLED(CONFIG_IPV6)
153-
struct vxlan_sock *vn6_sock; /* listening socket for IPv6 */
153+
struct vxlan_sock __rcu *vn6_sock; /* listening socket for IPv6 */
154154
#endif
155155
struct net_device *dev;
156156
struct net *net; /* netns for packet i/o */

0 commit comments

Comments
 (0)