diff options
author | pravin shelar <pshelar@ovn.org> | 2016-10-28 12:59:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-10-29 20:56:31 -0400 |
commit | c6fcc4fc5f8b592600c7409e769ab68da0fb1eca (patch) | |
tree | 4fafb69c92e2169209dfb66452b7d92e8abfb11c | |
parent | 087892d29b75c025086d99b29d385a3dac0169fc (diff) |
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>
-rw-r--r-- | drivers/net/vxlan.c | 80 | ||||
-rw-r--r-- | include/net/vxlan.h | 4 |
2 files changed, 52 insertions, 32 deletions
diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index c1639a3e95a4..f3c2fa3ab0d5 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c | |||
@@ -943,17 +943,20 @@ static bool vxlan_snoop(struct net_device *dev, | |||
943 | static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev) | 943 | static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev) |
944 | { | 944 | { |
945 | struct vxlan_dev *vxlan; | 945 | struct vxlan_dev *vxlan; |
946 | struct vxlan_sock *sock4; | ||
947 | struct vxlan_sock *sock6 = NULL; | ||
946 | unsigned short family = dev->default_dst.remote_ip.sa.sa_family; | 948 | unsigned short family = dev->default_dst.remote_ip.sa.sa_family; |
947 | 949 | ||
950 | sock4 = rtnl_dereference(dev->vn4_sock); | ||
951 | |||
948 | /* The vxlan_sock is only used by dev, leaving group has | 952 | /* The vxlan_sock is only used by dev, leaving group has |
949 | * no effect on other vxlan devices. | 953 | * no effect on other vxlan devices. |
950 | */ | 954 | */ |
951 | if (family == AF_INET && dev->vn4_sock && | 955 | if (family == AF_INET && sock4 && atomic_read(&sock4->refcnt) == 1) |
952 | atomic_read(&dev->vn4_sock->refcnt) == 1) | ||
953 | return false; | 956 | return false; |
954 | #if IS_ENABLED(CONFIG_IPV6) | 957 | #if IS_ENABLED(CONFIG_IPV6) |
955 | if (family == AF_INET6 && dev->vn6_sock && | 958 | sock6 = rtnl_dereference(dev->vn6_sock); |
956 | atomic_read(&dev->vn6_sock->refcnt) == 1) | 959 | if (family == AF_INET6 && sock6 && atomic_read(&sock6->refcnt) == 1) |
957 | return false; | 960 | return false; |
958 | #endif | 961 | #endif |
959 | 962 | ||
@@ -961,10 +964,12 @@ static bool vxlan_group_used(struct vxlan_net *vn, struct vxlan_dev *dev) | |||
961 | if (!netif_running(vxlan->dev) || vxlan == dev) | 964 | if (!netif_running(vxlan->dev) || vxlan == dev) |
962 | continue; | 965 | continue; |
963 | 966 | ||
964 | if (family == AF_INET && vxlan->vn4_sock != dev->vn4_sock) | 967 | if (family == AF_INET && |
968 | rtnl_dereference(vxlan->vn4_sock) != sock4) | ||
965 | continue; | 969 | continue; |
966 | #if IS_ENABLED(CONFIG_IPV6) | 970 | #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) | ||
968 | continue; | 973 | continue; |
969 | #endif | 974 | #endif |
970 | 975 | ||
@@ -1005,22 +1010,25 @@ static bool __vxlan_sock_release_prep(struct vxlan_sock *vs) | |||
1005 | 1010 | ||
1006 | static void vxlan_sock_release(struct vxlan_dev *vxlan) | 1011 | static void vxlan_sock_release(struct vxlan_dev *vxlan) |
1007 | { | 1012 | { |
1008 | bool ipv4 = __vxlan_sock_release_prep(vxlan->vn4_sock); | 1013 | struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); |
1009 | #if IS_ENABLED(CONFIG_IPV6) | 1014 | #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); | ||
1011 | #endif | 1018 | #endif |
1012 | 1019 | ||
1020 | rcu_assign_pointer(vxlan->vn4_sock, NULL); | ||
1013 | synchronize_net(); | 1021 | synchronize_net(); |
1014 | 1022 | ||
1015 | if (ipv4) { | 1023 | if (__vxlan_sock_release_prep(sock4)) { |
1016 | udp_tunnel_sock_release(vxlan->vn4_sock->sock); | 1024 | udp_tunnel_sock_release(sock4->sock); |
1017 | kfree(vxlan->vn4_sock); | 1025 | kfree(sock4); |
1018 | } | 1026 | } |
1019 | 1027 | ||
1020 | #if IS_ENABLED(CONFIG_IPV6) | 1028 | #if IS_ENABLED(CONFIG_IPV6) |
1021 | if (ipv6) { | 1029 | if (__vxlan_sock_release_prep(sock6)) { |
1022 | udp_tunnel_sock_release(vxlan->vn6_sock->sock); | 1030 | udp_tunnel_sock_release(sock6->sock); |
1023 | kfree(vxlan->vn6_sock); | 1031 | kfree(sock6); |
1024 | } | 1032 | } |
1025 | #endif | 1033 | #endif |
1026 | } | 1034 | } |
@@ -1036,18 +1044,21 @@ static int vxlan_igmp_join(struct vxlan_dev *vxlan) | |||
1036 | int ret = -EINVAL; | 1044 | int ret = -EINVAL; |
1037 | 1045 | ||
1038 | if (ip->sa.sa_family == AF_INET) { | 1046 | if (ip->sa.sa_family == AF_INET) { |
1047 | struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); | ||
1039 | struct ip_mreqn mreq = { | 1048 | struct ip_mreqn mreq = { |
1040 | .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, | 1049 | .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, |
1041 | .imr_ifindex = ifindex, | 1050 | .imr_ifindex = ifindex, |
1042 | }; | 1051 | }; |
1043 | 1052 | ||
1044 | sk = vxlan->vn4_sock->sock->sk; | 1053 | sk = sock4->sock->sk; |
1045 | lock_sock(sk); | 1054 | lock_sock(sk); |
1046 | ret = ip_mc_join_group(sk, &mreq); | 1055 | ret = ip_mc_join_group(sk, &mreq); |
1047 | release_sock(sk); | 1056 | release_sock(sk); |
1048 | #if IS_ENABLED(CONFIG_IPV6) | 1057 | #if IS_ENABLED(CONFIG_IPV6) |
1049 | } else { | 1058 | } else { |
1050 | sk = vxlan->vn6_sock->sock->sk; | 1059 | struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); |
1060 | |||
1061 | sk = sock6->sock->sk; | ||
1051 | lock_sock(sk); | 1062 | lock_sock(sk); |
1052 | ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex, | 1063 | ret = ipv6_stub->ipv6_sock_mc_join(sk, ifindex, |
1053 | &ip->sin6.sin6_addr); | 1064 | &ip->sin6.sin6_addr); |
@@ -1067,18 +1078,21 @@ static int vxlan_igmp_leave(struct vxlan_dev *vxlan) | |||
1067 | int ret = -EINVAL; | 1078 | int ret = -EINVAL; |
1068 | 1079 | ||
1069 | if (ip->sa.sa_family == AF_INET) { | 1080 | if (ip->sa.sa_family == AF_INET) { |
1081 | struct vxlan_sock *sock4 = rtnl_dereference(vxlan->vn4_sock); | ||
1070 | struct ip_mreqn mreq = { | 1082 | struct ip_mreqn mreq = { |
1071 | .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, | 1083 | .imr_multiaddr.s_addr = ip->sin.sin_addr.s_addr, |
1072 | .imr_ifindex = ifindex, | 1084 | .imr_ifindex = ifindex, |
1073 | }; | 1085 | }; |
1074 | 1086 | ||
1075 | sk = vxlan->vn4_sock->sock->sk; | 1087 | sk = sock4->sock->sk; |
1076 | lock_sock(sk); | 1088 | lock_sock(sk); |
1077 | ret = ip_mc_leave_group(sk, &mreq); | 1089 | ret = ip_mc_leave_group(sk, &mreq); |
1078 | release_sock(sk); | 1090 | release_sock(sk); |
1079 | #if IS_ENABLED(CONFIG_IPV6) | 1091 | #if IS_ENABLED(CONFIG_IPV6) |
1080 | } else { | 1092 | } else { |
1081 | sk = vxlan->vn6_sock->sock->sk; | 1093 | struct vxlan_sock *sock6 = rtnl_dereference(vxlan->vn6_sock); |
1094 | |||
1095 | sk = sock6->sock->sk; | ||
1082 | lock_sock(sk); | 1096 | lock_sock(sk); |
1083 | ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex, | 1097 | ret = ipv6_stub->ipv6_sock_mc_drop(sk, ifindex, |
1084 | &ip->sin6.sin6_addr); | 1098 | &ip->sin6.sin6_addr); |
@@ -1828,11 +1842,15 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, | |||
1828 | struct dst_cache *dst_cache, | 1842 | struct dst_cache *dst_cache, |
1829 | const struct ip_tunnel_info *info) | 1843 | const struct ip_tunnel_info *info) |
1830 | { | 1844 | { |
1845 | struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); | ||
1831 | bool use_cache = ip_tunnel_dst_cache_usable(skb, info); | 1846 | bool use_cache = ip_tunnel_dst_cache_usable(skb, info); |
1832 | struct dst_entry *ndst; | 1847 | struct dst_entry *ndst; |
1833 | struct flowi6 fl6; | 1848 | struct flowi6 fl6; |
1834 | int err; | 1849 | int err; |
1835 | 1850 | ||
1851 | if (!sock6) | ||
1852 | return ERR_PTR(-EIO); | ||
1853 | |||
1836 | if (tos && !info) | 1854 | if (tos && !info) |
1837 | use_cache = false; | 1855 | use_cache = false; |
1838 | if (use_cache) { | 1856 | if (use_cache) { |
@@ -1850,7 +1868,7 @@ static struct dst_entry *vxlan6_get_route(struct vxlan_dev *vxlan, | |||
1850 | fl6.flowi6_proto = IPPROTO_UDP; | 1868 | fl6.flowi6_proto = IPPROTO_UDP; |
1851 | 1869 | ||
1852 | err = ipv6_stub->ipv6_dst_lookup(vxlan->net, | 1870 | err = ipv6_stub->ipv6_dst_lookup(vxlan->net, |
1853 | vxlan->vn6_sock->sock->sk, | 1871 | sock6->sock->sk, |
1854 | &ndst, &fl6); | 1872 | &ndst, &fl6); |
1855 | if (err < 0) | 1873 | if (err < 0) |
1856 | return ERR_PTR(err); | 1874 | return ERR_PTR(err); |
@@ -1995,9 +2013,11 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, | |||
1995 | } | 2013 | } |
1996 | 2014 | ||
1997 | if (dst->sa.sa_family == AF_INET) { | 2015 | 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) | ||
1999 | goto drop; | 2019 | goto drop; |
2000 | sk = vxlan->vn4_sock->sock->sk; | 2020 | sk = sock4->sock->sk; |
2001 | 2021 | ||
2002 | rt = vxlan_get_route(vxlan, skb, | 2022 | rt = vxlan_get_route(vxlan, skb, |
2003 | rdst ? rdst->remote_ifindex : 0, tos, | 2023 | rdst ? rdst->remote_ifindex : 0, tos, |
@@ -2050,12 +2070,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, | |||
2050 | src_port, dst_port, xnet, !udp_sum); | 2070 | src_port, dst_port, xnet, !udp_sum); |
2051 | #if IS_ENABLED(CONFIG_IPV6) | 2071 | #if IS_ENABLED(CONFIG_IPV6) |
2052 | } else { | 2072 | } else { |
2073 | struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); | ||
2053 | struct dst_entry *ndst; | 2074 | struct dst_entry *ndst; |
2054 | u32 rt6i_flags; | 2075 | u32 rt6i_flags; |
2055 | 2076 | ||
2056 | if (!vxlan->vn6_sock) | 2077 | if (!sock6) |
2057 | goto drop; | 2078 | goto drop; |
2058 | sk = vxlan->vn6_sock->sock->sk; | 2079 | sk = sock6->sock->sk; |
2059 | 2080 | ||
2060 | ndst = vxlan6_get_route(vxlan, skb, | 2081 | ndst = vxlan6_get_route(vxlan, skb, |
2061 | rdst ? rdst->remote_ifindex : 0, tos, | 2082 | rdst ? rdst->remote_ifindex : 0, tos, |
@@ -2415,9 +2436,10 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) | |||
2415 | dport = info->key.tp_dst ? : vxlan->cfg.dst_port; | 2436 | dport = info->key.tp_dst ? : vxlan->cfg.dst_port; |
2416 | 2437 | ||
2417 | if (ip_tunnel_info_af(info) == AF_INET) { | 2438 | if (ip_tunnel_info_af(info) == AF_INET) { |
2439 | struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); | ||
2418 | struct rtable *rt; | 2440 | struct rtable *rt; |
2419 | 2441 | ||
2420 | if (!vxlan->vn4_sock) | 2442 | if (!sock4) |
2421 | return -EINVAL; | 2443 | return -EINVAL; |
2422 | rt = vxlan_get_route(vxlan, skb, 0, info->key.tos, | 2444 | rt = vxlan_get_route(vxlan, skb, 0, info->key.tos, |
2423 | info->key.u.ipv4.dst, | 2445 | info->key.u.ipv4.dst, |
@@ -2429,8 +2451,6 @@ static int vxlan_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) | |||
2429 | #if IS_ENABLED(CONFIG_IPV6) | 2451 | #if IS_ENABLED(CONFIG_IPV6) |
2430 | struct dst_entry *ndst; | 2452 | struct dst_entry *ndst; |
2431 | 2453 | ||
2432 | if (!vxlan->vn6_sock) | ||
2433 | return -EINVAL; | ||
2434 | ndst = vxlan6_get_route(vxlan, skb, 0, info->key.tos, | 2454 | ndst = vxlan6_get_route(vxlan, skb, 0, info->key.tos, |
2435 | info->key.label, &info->key.u.ipv6.dst, | 2455 | info->key.label, &info->key.u.ipv6.dst, |
2436 | &info->key.u.ipv6.src, NULL, info); | 2456 | &info->key.u.ipv6.src, NULL, info); |
@@ -2740,10 +2760,10 @@ static int __vxlan_sock_add(struct vxlan_dev *vxlan, bool ipv6) | |||
2740 | return PTR_ERR(vs); | 2760 | return PTR_ERR(vs); |
2741 | #if IS_ENABLED(CONFIG_IPV6) | 2761 | #if IS_ENABLED(CONFIG_IPV6) |
2742 | if (ipv6) | 2762 | if (ipv6) |
2743 | vxlan->vn6_sock = vs; | 2763 | rcu_assign_pointer(vxlan->vn6_sock, vs); |
2744 | else | 2764 | else |
2745 | #endif | 2765 | #endif |
2746 | vxlan->vn4_sock = vs; | 2766 | rcu_assign_pointer(vxlan->vn4_sock, vs); |
2747 | vxlan_vs_add_dev(vs, vxlan); | 2767 | vxlan_vs_add_dev(vs, vxlan); |
2748 | return 0; | 2768 | return 0; |
2749 | } | 2769 | } |
@@ -2754,9 +2774,9 @@ static int vxlan_sock_add(struct vxlan_dev *vxlan) | |||
2754 | bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA; | 2774 | bool metadata = vxlan->flags & VXLAN_F_COLLECT_METADATA; |
2755 | int ret = 0; | 2775 | int ret = 0; |
2756 | 2776 | ||
2757 | vxlan->vn4_sock = NULL; | 2777 | RCU_INIT_POINTER(vxlan->vn4_sock, NULL); |
2758 | #if IS_ENABLED(CONFIG_IPV6) | 2778 | #if IS_ENABLED(CONFIG_IPV6) |
2759 | vxlan->vn6_sock = NULL; | 2779 | RCU_INIT_POINTER(vxlan->vn6_sock, NULL); |
2760 | if (ipv6 || metadata) | 2780 | if (ipv6 || metadata) |
2761 | ret = __vxlan_sock_add(vxlan, true); | 2781 | ret = __vxlan_sock_add(vxlan, true); |
2762 | #endif | 2782 | #endif |
diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 0255613a54a4..308adc4154f4 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h | |||
@@ -225,9 +225,9 @@ struct vxlan_config { | |||
225 | struct vxlan_dev { | 225 | struct vxlan_dev { |
226 | struct hlist_node hlist; /* vni hash table */ | 226 | struct hlist_node hlist; /* vni hash table */ |
227 | struct list_head next; /* vxlan's per namespace list */ | 227 | 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 */ |
229 | #if IS_ENABLED(CONFIG_IPV6) | 229 | #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 */ |
231 | #endif | 231 | #endif |
232 | struct net_device *dev; | 232 | struct net_device *dev; |
233 | struct net *net; /* netns for packet i/o */ | 233 | struct net *net; /* netns for packet i/o */ |