diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 9710e8958b38..dd0638d3c3f9 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1622,12 +1622,20 @@ static void mroute_clean_tables(struct mr_table *mrt, int flags, } } -static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk) +static int ip6mr_sk_init(struct sock *sk) { - int err = 0; struct net *net = sock_net(sk); + struct mr_table *mrt; + int err = 0; rtnl_lock(); + mrt = __ip6mr_get_table(net, + raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (!mrt) { + err = -ENOENT; + goto out_unlock; + } + spin_lock(&mrt_lock); if (rtnl_dereference(mrt->mroute_sk)) { err = -EADDRINUSE; @@ -1643,6 +1651,7 @@ static int ip6mr_sk_init(struct mr_table *mrt, struct sock *sk) NETCONFA_MC_FORWARDING, NETCONFA_IFINDEX_ALL, net->ipv6.devconf_all); +out_unlock: rtnl_unlock(); return err; @@ -1744,7 +1753,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, if (optlen < sizeof(int)) return -EINVAL; - return ip6mr_sk_init(mrt, sk); + return ip6mr_sk_init(sk); case MRT6_DONE: return ip6mr_sk_done(sk); @@ -1871,16 +1880,21 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, sockptr_t optval, /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */ if (v != RT_TABLE_DEFAULT && v >= 100000000) return -EINVAL; - if (sk == rcu_access_pointer(mrt->mroute_sk)) - return -EBUSY; - rtnl_lock(); ret = 0; - mrt = ip6mr_new_table(net, v); - if (IS_ERR(mrt)) - ret = PTR_ERR(mrt); - else - raw6_sk(sk)->ip6mr_table = v; + mrt = __ip6mr_get_table(net, + raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (!mrt) { + ret = -ENOENT; + } else if (sk == rtnl_dereference(mrt->mroute_sk)) { + ret = -EBUSY; + } else { + mrt = ip6mr_new_table(net, v); + if (IS_ERR(mrt)) + ret = PTR_ERR(mrt); + else + raw6_sk(sk)->ip6mr_table = v; + } rtnl_unlock(); return ret; }