aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuval Mintz <yuvalm@mellanox.com>2018-02-28 16:29:30 -0500
committerDavid S. Miller <davem@davemloft.net>2018-03-01 13:13:23 -0500
commit8571ab479a6e1ef46ead5ebee567e128a422767c (patch)
tree3e5db39b21f5835626ad4cad5bde3bc46c6db988
parent6853f21f764b04e58df5e44629fec1fb8f3cbf2e (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.h6
-rw-r--r--net/ipv6/ip6_output.c2
-rw-r--r--net/ipv6/ip6mr.c45
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
114extern struct sock *mroute6_socket(struct net *net, struct sk_buff *skb); 114bool mroute6_is_socket(struct net *net, struct sk_buff *skb);
115extern int ip6mr_sk_done(struct sock *sk); 115extern int ip6mr_sk_done(struct sock *sk);
116#else 116#else
117static inline struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) 117static inline bool mroute6_is_socket(struct net *net, struct sk_buff *skb)
118{ 118{
119 return NULL; 119 return false;
120} 120}
121static inline int ip6mr_sk_done(struct sock *sk) 121static 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,
1121static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt, 1121static 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
1637struct sock *mroute6_socket(struct net *net, struct sk_buff *skb) 1641bool 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}
1655EXPORT_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();