diff options
author | Yuval Mintz <yuvalm@mellanox.com> | 2018-02-28 16:29:30 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-03-01 13:13:23 -0500 |
commit | 8571ab479a6e1ef46ead5ebee567e128a422767c (patch) | |
tree | 3e5db39b21f5835626ad4cad5bde3bc46c6db988 | |
parent | 6853f21f764b04e58df5e44629fec1fb8f3cbf2e (diff) |
ip6mr: Make mroute_sk rcu-based
In ipmr the mr_table socket is handled under RCU. Introduce the same
for ip6mr.
Signed-off-by: Yuval Mintz <yuvalm@mellanox.com>
Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/mroute6.h | 6 | ||||
-rw-r--r-- | net/ipv6/ip6_output.c | 2 | ||||
-rw-r--r-- | net/ipv6/ip6mr.c | 45 |
3 files changed, 31 insertions, 22 deletions
diff --git a/include/linux/mroute6.h b/include/linux/mroute6.h index e5e5b8282551..e1b9fb06e1ea 100644 --- a/include/linux/mroute6.h +++ b/include/linux/mroute6.h | |||
@@ -111,12 +111,12 @@ extern int ip6mr_get_route(struct net *net, struct sk_buff *skb, | |||
111 | struct rtmsg *rtm, u32 portid); | 111 | struct rtmsg *rtm, u32 portid); |
112 | 112 | ||
113 | #ifdef CONFIG_IPV6_MROUTE | 113 | #ifdef CONFIG_IPV6_MROUTE |
114 | extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb); | 114 | bool mroute6_is_socket(struct net *net, struct sk_buff *skb); |
115 | extern int ip6mr_sk_done(struct sock *sk); | 115 | extern int ip6mr_sk_done(struct sock *sk); |
116 | #else | 116 | #else |
117 | static inline struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) | 117 | static inline bool mroute6_is_socket(struct net *net, struct sk_buff *skb) |
118 | { | 118 | { |
119 | return NULL; | 119 | return false; |
120 | } | 120 | } |
121 | static inline int ip6mr_sk_done(struct sock *sk) | 121 | static inline int ip6mr_sk_done(struct sock *sk) |
122 | { | 122 | { |
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 997c7f19ad62..a6eb0e699b15 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -71,7 +71,7 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff * | |||
71 | struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); | 71 | struct inet6_dev *idev = ip6_dst_idev(skb_dst(skb)); |
72 | 72 | ||
73 | if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) && | 73 | if (!(dev->flags & IFF_LOOPBACK) && sk_mc_loop(sk) && |
74 | ((mroute6_socket(net, skb) && | 74 | ((mroute6_is_socket(net, skb) && |
75 | !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || | 75 | !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) || |
76 | ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, | 76 | ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr, |
77 | &ipv6_hdr(skb)->saddr))) { | 77 | &ipv6_hdr(skb)->saddr))) { |
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index e397990f6eb8..a0e297ddca6e 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c | |||
@@ -58,7 +58,7 @@ struct mr6_table { | |||
58 | struct list_head list; | 58 | struct list_head list; |
59 | possible_net_t net; | 59 | possible_net_t net; |
60 | u32 id; | 60 | u32 id; |
61 | struct sock *mroute6_sk; | 61 | struct sock __rcu *mroute6_sk; |
62 | struct timer_list ipmr_expire_timer; | 62 | struct timer_list ipmr_expire_timer; |
63 | struct list_head mfc6_unres_queue; | 63 | struct list_head mfc6_unres_queue; |
64 | struct list_head mfc6_cache_array[MFC6_LINES]; | 64 | struct list_head mfc6_cache_array[MFC6_LINES]; |
@@ -1121,6 +1121,7 @@ static void ip6mr_cache_resolve(struct net *net, struct mr6_table *mrt, | |||
1121 | static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, | 1121 | static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, |
1122 | mifi_t mifi, int assert) | 1122 | mifi_t mifi, int assert) |
1123 | { | 1123 | { |
1124 | struct sock *mroute6_sk; | ||
1124 | struct sk_buff *skb; | 1125 | struct sk_buff *skb; |
1125 | struct mrt6msg *msg; | 1126 | struct mrt6msg *msg; |
1126 | int ret; | 1127 | int ret; |
@@ -1190,17 +1191,19 @@ static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, | |||
1190 | skb->ip_summed = CHECKSUM_UNNECESSARY; | 1191 | skb->ip_summed = CHECKSUM_UNNECESSARY; |
1191 | } | 1192 | } |
1192 | 1193 | ||
1193 | if (!mrt->mroute6_sk) { | 1194 | rcu_read_lock(); |
1195 | mroute6_sk = rcu_dereference(mrt->mroute6_sk); | ||
1196 | if (!mroute6_sk) { | ||
1197 | rcu_read_unlock(); | ||
1194 | kfree_skb(skb); | 1198 | kfree_skb(skb); |
1195 | return -EINVAL; | 1199 | return -EINVAL; |
1196 | } | 1200 | } |
1197 | 1201 | ||
1198 | mrt6msg_netlink_event(mrt, skb); | 1202 | mrt6msg_netlink_event(mrt, skb); |
1199 | 1203 | ||
1200 | /* | 1204 | /* Deliver to user space multicast routing algorithms */ |
1201 | * Deliver to user space multicast routing algorithms | 1205 | ret = sock_queue_rcv_skb(mroute6_sk, skb); |
1202 | */ | 1206 | rcu_read_unlock(); |
1203 | ret = sock_queue_rcv_skb(mrt->mroute6_sk, skb); | ||
1204 | if (ret < 0) { | 1207 | if (ret < 0) { |
1205 | net_warn_ratelimited("mroute6: pending queue full, dropping entries\n"); | 1208 | net_warn_ratelimited("mroute6: pending queue full, dropping entries\n"); |
1206 | kfree_skb(skb); | 1209 | kfree_skb(skb); |
@@ -1584,11 +1587,11 @@ static int ip6mr_sk_init(struct mr6_table *mrt, struct sock *sk) | |||
1584 | 1587 | ||
1585 | rtnl_lock(); | 1588 | rtnl_lock(); |
1586 | write_lock_bh(&mrt_lock); | 1589 | write_lock_bh(&mrt_lock); |
1587 | if (likely(mrt->mroute6_sk == NULL)) { | 1590 | if (rtnl_dereference(mrt->mroute6_sk)) { |
1588 | mrt->mroute6_sk = sk; | ||
1589 | net->ipv6.devconf_all->mc_forwarding++; | ||
1590 | } else { | ||
1591 | err = -EADDRINUSE; | 1591 | err = -EADDRINUSE; |
1592 | } else { | ||
1593 | rcu_assign_pointer(mrt->mroute6_sk, sk); | ||
1594 | net->ipv6.devconf_all->mc_forwarding++; | ||
1592 | } | 1595 | } |
1593 | write_unlock_bh(&mrt_lock); | 1596 | write_unlock_bh(&mrt_lock); |
1594 | 1597 | ||
@@ -1614,9 +1617,9 @@ int ip6mr_sk_done(struct sock *sk) | |||
1614 | 1617 | ||
1615 | rtnl_lock(); | 1618 | rtnl_lock(); |
1616 | ip6mr_for_each_table(mrt, net) { | 1619 | ip6mr_for_each_table(mrt, net) { |
1617 | if (sk == mrt->mroute6_sk) { | 1620 | if (sk == rtnl_dereference(mrt->mroute6_sk)) { |
1618 | write_lock_bh(&mrt_lock); | 1621 | write_lock_bh(&mrt_lock); |
1619 | mrt->mroute6_sk = NULL; | 1622 | RCU_INIT_POINTER(mrt->mroute6_sk, NULL); |
1620 | net->ipv6.devconf_all->mc_forwarding--; | 1623 | net->ipv6.devconf_all->mc_forwarding--; |
1621 | write_unlock_bh(&mrt_lock); | 1624 | write_unlock_bh(&mrt_lock); |
1622 | inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, | 1625 | inet6_netconf_notify_devconf(net, RTM_NEWNETCONF, |
@@ -1630,11 +1633,12 @@ int ip6mr_sk_done(struct sock *sk) | |||
1630 | } | 1633 | } |
1631 | } | 1634 | } |
1632 | rtnl_unlock(); | 1635 | rtnl_unlock(); |
1636 | synchronize_rcu(); | ||
1633 | 1637 | ||
1634 | return err; | 1638 | return err; |
1635 | } | 1639 | } |
1636 | 1640 | ||
1637 | struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) | 1641 | bool mroute6_is_socket(struct net *net, struct sk_buff *skb) |
1638 | { | 1642 | { |
1639 | struct mr6_table *mrt; | 1643 | struct mr6_table *mrt; |
1640 | struct flowi6 fl6 = { | 1644 | struct flowi6 fl6 = { |
@@ -1646,8 +1650,9 @@ struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) | |||
1646 | if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0) | 1650 | if (ip6mr_fib_lookup(net, &fl6, &mrt) < 0) |
1647 | return NULL; | 1651 | return NULL; |
1648 | 1652 | ||
1649 | return mrt->mroute6_sk; | 1653 | return rcu_access_pointer(mrt->mroute6_sk); |
1650 | } | 1654 | } |
1655 | EXPORT_SYMBOL(mroute6_is_socket); | ||
1651 | 1656 | ||
1652 | /* | 1657 | /* |
1653 | * Socket options and virtual interface manipulation. The whole | 1658 | * Socket options and virtual interface manipulation. The whole |
@@ -1674,7 +1679,8 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns | |||
1674 | return -ENOENT; | 1679 | return -ENOENT; |
1675 | 1680 | ||
1676 | if (optname != MRT6_INIT) { | 1681 | if (optname != MRT6_INIT) { |
1677 | if (sk != mrt->mroute6_sk && !ns_capable(net->user_ns, CAP_NET_ADMIN)) | 1682 | if (sk != rcu_access_pointer(mrt->mroute6_sk) && |
1683 | !ns_capable(net->user_ns, CAP_NET_ADMIN)) | ||
1678 | return -EACCES; | 1684 | return -EACCES; |
1679 | } | 1685 | } |
1680 | 1686 | ||
@@ -1696,7 +1702,8 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns | |||
1696 | if (vif.mif6c_mifi >= MAXMIFS) | 1702 | if (vif.mif6c_mifi >= MAXMIFS) |
1697 | return -ENFILE; | 1703 | return -ENFILE; |
1698 | rtnl_lock(); | 1704 | rtnl_lock(); |
1699 | ret = mif6_add(net, mrt, &vif, sk == mrt->mroute6_sk); | 1705 | ret = mif6_add(net, mrt, &vif, |
1706 | sk == rtnl_dereference(mrt->mroute6_sk)); | ||
1700 | rtnl_unlock(); | 1707 | rtnl_unlock(); |
1701 | return ret; | 1708 | return ret; |
1702 | 1709 | ||
@@ -1731,7 +1738,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns | |||
1731 | ret = ip6mr_mfc_delete(mrt, &mfc, parent); | 1738 | ret = ip6mr_mfc_delete(mrt, &mfc, parent); |
1732 | else | 1739 | else |
1733 | ret = ip6mr_mfc_add(net, mrt, &mfc, | 1740 | ret = ip6mr_mfc_add(net, mrt, &mfc, |
1734 | sk == mrt->mroute6_sk, parent); | 1741 | sk == |
1742 | rtnl_dereference(mrt->mroute6_sk), | ||
1743 | parent); | ||
1735 | rtnl_unlock(); | 1744 | rtnl_unlock(); |
1736 | return ret; | 1745 | return ret; |
1737 | 1746 | ||
@@ -1783,7 +1792,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns | |||
1783 | /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */ | 1792 | /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */ |
1784 | if (v != RT_TABLE_DEFAULT && v >= 100000000) | 1793 | if (v != RT_TABLE_DEFAULT && v >= 100000000) |
1785 | return -EINVAL; | 1794 | return -EINVAL; |
1786 | if (sk == mrt->mroute6_sk) | 1795 | if (sk == rcu_access_pointer(mrt->mroute6_sk)) |
1787 | return -EBUSY; | 1796 | return -EBUSY; |
1788 | 1797 | ||
1789 | rtnl_lock(); | 1798 | rtnl_lock(); |