Skip to content

Commit c6fcc4f

Browse files
pshelardavem330
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. Signed-off-by: Pravin B Shelar <pshelar@ovn.org> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 087892d commit c6fcc4f

File tree

2 files changed

+52
-32
lines changed

2 files changed

+52
-32
lines changed

drivers/net/vxlan.c

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -943,28 +943,33 @@ static bool vxlan_snoop(struct net_device *dev,
943943
static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev)
944944
{
945945
struct vxlan_dev *vxlan;
946+
struct vxlan_sock *sock4;
947+
struct vxlan_sock *sock6 = NULL;
946948
unsigned short family = dev->default_dst.remote_ip.sa.sa_family;
947949

950+
sock4 = rtnl_dereference(dev->vn4_sock);
951+
948952
/* The vxlan_sock is only used by dev, leaving group has
949953
* no effect on other vxlan devices.
950954
*/
951-
if (family == AF_INET && dev->vn4_sock &&
952-
atomic_read(&dev->vn4_sock->refcnt) == 1)
955+
if (family == AF_INET && sock4 && atomic_read(&sock4->refcnt) == 1)
953956
return false;
954957
#if IS_ENABLED(CONFIG_IPV6)
955-
if (family == AF_INET6 && dev->vn6_sock &&
956-
atomic_read(&dev->vn6_sock->refcnt) == 1)
958+
sock6 = rtnl_dereference(dev->vn6_sock);
959+
if (family == AF_INET6 && sock6 && atomic_read(&sock6->refcnt) == 1)
957960
return false;
958961
#endif
959962

960963
list_for_each_entry(vxlan, &vn->vxlan_list, next) {
961964
if (!netif_running(vxlan->dev) || vxlan == dev)
962965
continue;
963966

964-
if (family == AF_INET && vxlan->vn4_sock != dev->vn4_sock)
967+
if (family == AF_INET &&
968+
rtnl_dereference(vxlan->vn4_sock) != sock4)
965969
continue;
966970
#if IS_ENABLED(CONFIG_IPV6)
967-
if (family == AF_INET6 && vxlan->vn6_sock != dev->vn6_sock)
971+
if (family == AF_INET6 &&
972+
rtnl_dereference(vxlan->vn6_sock) != sock6)
968973
continue;
969974
#endif
970975

@@ -1005,22 +1010,25 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs)
10051010

10061011
static void vxlan_sock_release(struct vxlan_dev *vxlan)
10071012
{
1008-
bool ipv4 = __vxlan_sock_release_prep(vxlan->vn4_sock);
1013+
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
10091014
#if IS_ENABLED(CONFIG_IPV6)
1010-
bool ipv6 = __vxlan_sock_release_prep(vxlan->vn6_sock);
1015+
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
1016+
1017+
rcu_assign_pointer(vxlan->vn6_sock, NULL);
10111018
#endif
10121019

1020+
rcu_assign_pointer(vxlan->vn4_sock, NULL);
10131021
synchronize_net();
10141022

1015-
if (ipv4) {
1016-
udp_tunnel_sock_release(vxlan->vn4_sock->sock);
1017-
kfree(vxlan->vn4_sock);
1023+
if (__vxlan_sock_release_prep(sock4)) {
1024+
udp_tunnel_sock_release(sock4->sock);
1025+
kfree(sock4);
10181026
}
10191027

10201028
#if IS_ENABLED(CONFIG_IPV6)
1021-
if (ipv6) {
1022-
udp_tunnel_sock_release(vxlan->vn6_sock->sock);
1023-
kfree(vxlan->vn6_sock);
1029+
if (__vxlan_sock_release_prep(sock6)) {
1030+
udp_tunnel_sock_release(sock6->sock);
1031+
kfree(sock6);
10241032
}
10251033
#endif
10261034
}
@@ -1036,18 +1044,21 @@ static int vxlan_igmp_join(struct vxlan_dev *vxlan)
10361044
int ret = -EINVAL;
10371045

10381046
if (ip->sa.sa_family == AF_INET) {
1047+
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
10391048
struct ip_mreqn mreq = {
10401049
.imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
10411050
.imr_ifindex = ifindex,
10421051
};
10431052

1044-
sk = vxlan->vn4_sock->sock->sk;
1053+
sk = sock4->sock->sk;
10451054
lock_sock(sk);
10461055
ret = ip_mc_join_group(sk, &mreq);
10471056
release_sock(sk);
10481057
#if IS_ENABLED(CONFIG_IPV6)
10491058
} else {
1050-
sk = vxlan->vn6_sock->sock->sk;
1059+
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
1060+
1061+
sk = sock6->sock->sk;
10511062
lock_sock(sk);
10521063
ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex,
10531064
&ip->sin6.sin6_addr);
@@ -1067,18 +1078,21 @@ static int vxlan_igmp_leave(struct vxlan_dev *vxlan)
10671078
int ret = -EINVAL;
10681079

10691080
if (ip->sa.sa_family == AF_INET) {
1081+
struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock);
10701082
struct ip_mreqn mreq = {
10711083
.imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr,
10721084
.imr_ifindex = ifindex,
10731085
};
10741086

1075-
sk = vxlan->vn4_sock->sock->sk;
1087+
sk = sock4->sock->sk;
10761088
lock_sock(sk);
10771089
ret = ip_mc_leave_group(sk, &mreq);
10781090
release_sock(sk);
10791091
#if IS_ENABLED(CONFIG_IPV6)
10801092
} else {
1081-
sk = vxlan->vn6_sock->sock->sk;
1093+
struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock);
1094+
1095+
sk = sock6->sock->sk;
10821096
lock_sock(sk);
10831097
ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex,
10841098
&ip->sin6.sin6_addr);
@@ -1828,11 +1842,15 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan,
18281842
struct dst_cache *dst_cache,
18291843
const struct ip_tunnel_info *info)
18301844
{
1845+
struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
18311846
bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
18321847
struct dst_entry *ndst;
18331848
struct flowi6 fl6;
18341849
int err;
18351850

1851+
if (!sock6)
1852+
return ERR_PTR(-EIO);
1853+
18361854
if (tos && !info)
18371855
use_cache = false;
18381856
if (use_cache) {
@@ -1850,7 +1868,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan,
18501868
fl6.flowi6_proto = IPPROTO_UDP;
18511869

18521870
err = ipv6_stub->ipv6_dst_lookup(vxlan->net,
1853-
vxlan->vn6_sock->sock->sk,
1871+
sock6->sock->sk,
18541872
&ndst, &fl6);
18551873
if (err < 0)
18561874
return ERR_PTR(err);
@@ -1995,9 +2013,11 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
19952013
}
19962014

19972015
if (dst->sa.sa_family == AF_INET) {
1998-
if (!vxlan->vn4_sock)
2016+
struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
2017+
2018+
if (!sock4)
19992019
goto drop;
2000-
sk = vxlan->vn4_sock->sock->sk;
2020+
sk = sock4->sock->sk;
20012021

20022022
rt = vxlan_get_route(vxlan, skb,
20032023
rdst ? rdst->remote_ifindex : 0, tos,
@@ -2050,12 +2070,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
20502070
src_port, dst_port, xnet, !udp_sum);
20512071
#if IS_ENABLED(CONFIG_IPV6)
20522072
} else {
2073+
struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
20532074
struct dst_entry *ndst;
20542075
u32 rt6i_flags;
20552076

2056-
if (!vxlan->vn6_sock)
2077+
if (!sock6)
20572078
goto drop;
2058-
sk = vxlan->vn6_sock->sock->sk;
2079+
sk = sock6->sock->sk;
20592080

20602081
ndst = vxlan6_get_route(vxlan, skb,
20612082
rdst ? rdst->remote_ifindex : 0, tos,
@@ -2415,9 +2436,10 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
24152436
dport = info->key.tp_dst ? : vxlan->cfg.dst_port;
24162437

24172438
if (ip_tunnel_info_af(info) == AF_INET) {
2439+
struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
24182440
struct rtable *rt;
24192441

2420-
if (!vxlan->vn4_sock)
2442+
if (!sock4)
24212443
return -EINVAL;
24222444
rt = vxlan_get_route(vxlan, skb, 0, info->key.tos,
24232445
info->key.u.ipv4.dst,
@@ -2429,8 +2451,6 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb)
24292451
#if IS_ENABLED(CONFIG_IPV6)
24302452
struct dst_entry *ndst;
24312453

2432-
if (!vxlan->vn6_sock)
2433-
return -EINVAL;
24342454
ndst = vxlan6_get_route(vxlan, skb, 0, info->key.tos,
24352455
info->key.label, &info->key.u.ipv6.dst,
24362456
&info->key.u.ipv6.src, NULL, info);
@@ -2740,10 +2760,10 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6)
27402760
return PTR_ERR(vs);
27412761
#if IS_ENABLED(CONFIG_IPV6)
27422762
if (ipv6)
2743-
vxlan->vn6_sock = vs;
2763+
rcu_assign_pointer(vxlan->vn6_sock, vs);
27442764
else
27452765
#endif
2746-
vxlan->vn4_sock = vs;
2766+
rcu_assign_pointer(vxlan->vn4_sock, vs);
27472767
vxlan_vs_add_dev(vs, vxlan);
27482768
return 0;
27492769
}
@@ -2754,9 +2774,9 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan)
27542774
bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA;
27552775
int ret = 0;
27562776

2757-
vxlan->vn4_sock = NULL;
2777+
RCU_INIT_POINTER(vxlan->vn4_sock, NULL);
27582778
#if IS_ENABLED(CONFIG_IPV6)
2759-
vxlan->vn6_sock = NULL;
2779+
RCU_INIT_POINTER(vxlan->vn6_sock, NULL);
27602780
if (ipv6 || metadata)
27612781
ret = __vxlan_sock_add(vxlan, true);
27622782
#endif

include/net/vxlan.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ struct vxlan_config {
225225
struct vxlan_dev {
226226
struct hlist_node hlist; /* vni hash table */
227227
struct list_head next; /* vxlan's per namespace list */
228-
struct vxlan_sock *vn4_sock; /* listening socket for IPv4 */
228+
struct vxlan_sock __rcu *vn4_sock; /* listening socket for IPv4 */
229229
#if IS_ENABLED(CONFIG_IPV6)
230-
struct vxlan_sock *vn6_sock; /* listening socket for IPv6 */
230+
struct vxlan_sock __rcu *vn6_sock; /* listening socket for IPv6 */
231231
#endif
232232
struct net_device *dev;
233233
struct net *net; /* netns for packet i/o */

0 commit comments

Comments
 (0)