aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2015-02-27 16:25:30 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-27 16:25:30 -0500
commit7705f730372d65f73599f0b49e3249433bba55e8 (patch)
tree3cc79521cb6dbcd600384507329ecef93ae618ab
parent723b8e460d87e957f251dc5764f4ab86af6ab44e (diff)
parent93a714d6b53d87872e552dbb273544bdeaaf6e12 (diff)
Merge branch 'ip_level_multicast_join_leave'
Madhu Challa says: ==================== Multicast group join/leave at ip level This series enables configuring multicast group join/leave at ip level by extending the "ip address" command. It adds a new control socket mc_autojoin_sock and ifa_flag IFA_F_MCAUTOJOIN to invoke the corresponding igmp group join/leave api. Since the igmp group join/leave api takes the rtnl_lock the code had to be refactored by adding a shim layer prefixed by __ that can be invoked by code that already has the rtnl_lock. This way we avoid proliferation of work queues. The first patch in this series does the refactoring for igmp v6. Its based on igmp v4 changes that were added by Eric Dumazet. The second patch in this series does the group join/leave based on the setting of the IFA_F_MCAUTOJOIN flag. v5: - addressed comments from Daniel Borkmann. - removed blank line in patch 1/2 - removed unused variable, const arg in patch 2/2 v4: - addressed comments from Yoshifuji Hideaki. - Remove WARN_ON not needed because we return a value from v2. - addressed comments from Daniel Borkmann. - rename sock to mc_autojoin_sk - ip_mc_config() pass ifa so it needs one less argument. - igmp_net_{init|destroy}() use inet_ctl_sock_{create|destroy} - inet_rtm_newaddr() change scope of ret. - igmp_net_init() no need to initialize sock to NULL. v3: - addressed comments from David Miller. - fixed indentation and local variable order. v2: - addressed comments from Eric Dumazet. - removed workqueue and call __ip_mc_{join|leave}_group or __ipv6_sock_mc_{join|drop} ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ipv6.h8
-rw-r--r--include/net/netns/ipv4.h1
-rw-r--r--include/net/netns/ipv6.h1
-rw-r--r--include/uapi/linux/if_addr.h1
-rw-r--r--net/ipv4/devinet.c31
-rw-r--r--net/ipv4/igmp.c13
-rw-r--r--net/ipv6/addrconf.c38
-rw-r--r--net/ipv6/mcast.c60
8 files changed, 137 insertions, 16 deletions
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 4c9fe224d73b..b7673065c074 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -940,4 +940,12 @@ int ipv6_sysctl_register(void);
940void ipv6_sysctl_unregister(void); 940void ipv6_sysctl_unregister(void);
941#endif 941#endif
942 942
943int ipv6_sock_mc_join(struct sock *sk, int ifindex,
944 const struct in6_addr *addr);
945int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
946 const struct in6_addr *addr);
947int ipv6_sock_mc_drop(struct sock *sk, int ifindex,
948 const struct in6_addr *addr);
949int __ipv6_sock_mc_drop(struct sock *sk, int ifindex,
950 const struct in6_addr *addr);
943#endif /* _NET_IPV6_H */ 951#endif /* _NET_IPV6_H */
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index dbe225478adb..1b26c6c3fd7c 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -49,6 +49,7 @@ struct netns_ipv4 {
49 struct sock *fibnl; 49 struct sock *fibnl;
50 50
51 struct sock * __percpu *icmp_sk; 51 struct sock * __percpu *icmp_sk;
52 struct sock *mc_autojoin_sk;
52 53
53 struct inet_peer_base *peers; 54 struct inet_peer_base *peers;
54 struct tcpm_hash_bucket *tcp_metrics_hash; 55 struct tcpm_hash_bucket *tcp_metrics_hash;
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index 69ae41f2098c..ca0db12cd089 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -67,6 +67,7 @@ struct netns_ipv6 {
67 struct sock *ndisc_sk; 67 struct sock *ndisc_sk;
68 struct sock *tcp_sk; 68 struct sock *tcp_sk;
69 struct sock *igmp_sk; 69 struct sock *igmp_sk;
70 struct sock *mc_autojoin_sk;
70#ifdef CONFIG_IPV6_MROUTE 71#ifdef CONFIG_IPV6_MROUTE
71#ifndef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES 72#ifndef CONFIG_IPV6_MROUTE_MULTIPLE_TABLES
72 struct mr6_table *mrt6; 73 struct mr6_table *mrt6;
diff --git a/include/uapi/linux/if_addr.h b/include/uapi/linux/if_addr.h
index dea10a87dfd1..40fdfea39714 100644
--- a/include/uapi/linux/if_addr.h
+++ b/include/uapi/linux/if_addr.h
@@ -50,6 +50,7 @@ enum {
50#define IFA_F_PERMANENT 0x80 50#define IFA_F_PERMANENT 0x80
51#define IFA_F_MANAGETEMPADDR 0x100 51#define IFA_F_MANAGETEMPADDR 0x100
52#define IFA_F_NOPREFIXROUTE 0x200 52#define IFA_F_NOPREFIXROUTE 0x200
53#define IFA_F_MCAUTOJOIN 0x400
53 54
54struct ifa_cacheinfo { 55struct ifa_cacheinfo {
55 __u32 ifa_prefered; 56 __u32 ifa_prefered;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 3a8985c94581..5105759e4e00 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -548,6 +548,26 @@ struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
548 return NULL; 548 return NULL;
549} 549}
550 550
551static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa)
552{
553 struct ip_mreqn mreq = {
554 .imr_multiaddr.s_addr = ifa->ifa_address,
555 .imr_ifindex = ifa->ifa_dev->dev->ifindex,
556 };
557 int ret;
558
559 ASSERT_RTNL();
560
561 lock_sock(sk);
562 if (join)
563 ret = __ip_mc_join_group(sk, &mreq);
564 else
565 ret = __ip_mc_leave_group(sk, &mreq);
566 release_sock(sk);
567
568 return ret;
569}
570
551static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) 571static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
552{ 572{
553 struct net *net = sock_net(skb->sk); 573 struct net *net = sock_net(skb->sk);
@@ -584,6 +604,8 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
584 !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 604 !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
585 continue; 605 continue;
586 606
607 if (ipv4_is_multicast(ifa->ifa_address))
608 ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa);
587 __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 609 __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid);
588 return 0; 610 return 0;
589 } 611 }
@@ -838,6 +860,15 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
838 * userspace already relies on not having to provide this. 860 * userspace already relies on not having to provide this.
839 */ 861 */
840 set_ifa_lifetime(ifa, valid_lft, prefered_lft); 862 set_ifa_lifetime(ifa, valid_lft, prefered_lft);
863 if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) {
864 int ret = ip_mc_config(net->ipv4.mc_autojoin_sk,
865 true, ifa);
866
867 if (ret < 0) {
868 inet_free_ifa(ifa);
869 return ret;
870 }
871 }
841 return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); 872 return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid);
842 } else { 873 } else {
843 inet_free_ifa(ifa); 874 inet_free_ifa(ifa);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 4b1172d73e03..5cb1ef4ce292 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -97,6 +97,7 @@
97#include <net/route.h> 97#include <net/route.h>
98#include <net/sock.h> 98#include <net/sock.h>
99#include <net/checksum.h> 99#include <net/checksum.h>
100#include <net/inet_common.h>
100#include <linux/netfilter_ipv4.h> 101#include <linux/netfilter_ipv4.h>
101#ifdef CONFIG_IP_MROUTE 102#ifdef CONFIG_IP_MROUTE
102#include <linux/mroute.h> 103#include <linux/mroute.h>
@@ -2740,6 +2741,7 @@ static const struct file_operations igmp_mcf_seq_fops = {
2740static int __net_init igmp_net_init(struct net *net) 2741static int __net_init igmp_net_init(struct net *net)
2741{ 2742{
2742 struct proc_dir_entry *pde; 2743 struct proc_dir_entry *pde;
2744 int err;
2743 2745
2744 pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops); 2746 pde = proc_create("igmp", S_IRUGO, net->proc_net, &igmp_mc_seq_fops);
2745 if (!pde) 2747 if (!pde)
@@ -2748,8 +2750,18 @@ static int __net_init igmp_net_init(struct net *net)
2748 &igmp_mcf_seq_fops); 2750 &igmp_mcf_seq_fops);
2749 if (!pde) 2751 if (!pde)
2750 goto out_mcfilter; 2752 goto out_mcfilter;
2753 err = inet_ctl_sock_create(&net->ipv4.mc_autojoin_sk, AF_INET,
2754 SOCK_DGRAM, 0, net);
2755 if (err < 0) {
2756 pr_err("Failed to initialize the IGMP autojoin socket (err %d)\n",
2757 err);
2758 goto out_sock;
2759 }
2760
2751 return 0; 2761 return 0;
2752 2762
2763out_sock:
2764 remove_proc_entry("mcfilter", net->proc_net);
2753out_mcfilter: 2765out_mcfilter:
2754 remove_proc_entry("igmp", net->proc_net); 2766 remove_proc_entry("igmp", net->proc_net);
2755out_igmp: 2767out_igmp:
@@ -2760,6 +2772,7 @@ static void __net_exit igmp_net_exit(struct net *net)
2760{ 2772{
2761 remove_proc_entry("mcfilter", net->proc_net); 2773 remove_proc_entry("mcfilter", net->proc_net);
2762 remove_proc_entry("igmp", net->proc_net); 2774 remove_proc_entry("igmp", net->proc_net);
2775 inet_ctl_sock_destroy(net->ipv4.mc_autojoin_sk);
2763} 2776}
2764 2777
2765static struct pernet_operations igmp_net_ops = { 2778static struct pernet_operations igmp_net_ops = {
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 98e4a63d72bb..783bccfcc060 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -2464,6 +2464,23 @@ err_exit:
2464 return err; 2464 return err;
2465} 2465}
2466 2466
2467static int ipv6_mc_config(struct sock *sk, bool join,
2468 const struct in6_addr *addr, int ifindex)
2469{
2470 int ret;
2471
2472 ASSERT_RTNL();
2473
2474 lock_sock(sk);
2475 if (join)
2476 ret = __ipv6_sock_mc_join(sk, ifindex, addr);
2477 else
2478 ret = __ipv6_sock_mc_drop(sk, ifindex, addr);
2479 release_sock(sk);
2480
2481 return ret;
2482}
2483
2467/* 2484/*
2468 * Manual configuration of address on an interface 2485 * Manual configuration of address on an interface
2469 */ 2486 */
@@ -2476,10 +2493,10 @@ static int inet6_addr_add(struct net *net, int ifindex,
2476 struct inet6_ifaddr *ifp; 2493 struct inet6_ifaddr *ifp;
2477 struct inet6_dev *idev; 2494 struct inet6_dev *idev;
2478 struct net_device *dev; 2495 struct net_device *dev;
2496 unsigned long timeout;
2497 clock_t expires;
2479 int scope; 2498 int scope;
2480 u32 flags; 2499 u32 flags;
2481 clock_t expires;
2482 unsigned long timeout;
2483 2500
2484 ASSERT_RTNL(); 2501 ASSERT_RTNL();
2485 2502
@@ -2501,6 +2518,14 @@ static int inet6_addr_add(struct net *net, int ifindex,
2501 if (IS_ERR(idev)) 2518 if (IS_ERR(idev))
2502 return PTR_ERR(idev); 2519 return PTR_ERR(idev);
2503 2520
2521 if (ifa_flags & IFA_F_MCAUTOJOIN) {
2522 int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk,
2523 true, pfx, ifindex);
2524
2525 if (ret < 0)
2526 return ret;
2527 }
2528
2504 scope = ipv6_addr_scope(pfx); 2529 scope = ipv6_addr_scope(pfx);
2505 2530
2506 timeout = addrconf_timeout_fixup(valid_lft, HZ); 2531 timeout = addrconf_timeout_fixup(valid_lft, HZ);
@@ -2542,6 +2567,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
2542 in6_ifa_put(ifp); 2567 in6_ifa_put(ifp);
2543 addrconf_verify_rtnl(); 2568 addrconf_verify_rtnl();
2544 return 0; 2569 return 0;
2570 } else if (ifa_flags & IFA_F_MCAUTOJOIN) {
2571 ipv6_mc_config(net->ipv6.mc_autojoin_sk,
2572 false, pfx, ifindex);
2545 } 2573 }
2546 2574
2547 return PTR_ERR(ifp); 2575 return PTR_ERR(ifp);
@@ -2578,6 +2606,10 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
2578 jiffies); 2606 jiffies);
2579 ipv6_del_addr(ifp); 2607 ipv6_del_addr(ifp);
2580 addrconf_verify_rtnl(); 2608 addrconf_verify_rtnl();
2609 if (ipv6_addr_is_multicast(pfx)) {
2610 ipv6_mc_config(net->ipv6.mc_autojoin_sk,
2611 false, pfx, dev->ifindex);
2612 }
2581 return 0; 2613 return 0;
2582 } 2614 }
2583 } 2615 }
@@ -3945,7 +3977,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
3945 3977
3946 /* We ignore other flags so far. */ 3978 /* We ignore other flags so far. */
3947 ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | 3979 ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
3948 IFA_F_NOPREFIXROUTE; 3980 IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN;
3949 3981
3950 ifa = ipv6_get_ifaddr(net, pfx, dev, 1); 3982 ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
3951 if (ifa == NULL) { 3983 if (ifa == NULL) {
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 5ce107c8aab3..1dd1fedff9f4 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -132,7 +132,7 @@ static int unsolicited_report_interval(struct inet6_dev *idev)
132 return iv > 0 ? iv : 1; 132 return iv > 0 ? iv : 1;
133} 133}
134 134
135int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) 135int __ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
136{ 136{
137 struct net_device *dev = NULL; 137 struct net_device *dev = NULL;
138 struct ipv6_mc_socklist *mc_lst; 138 struct ipv6_mc_socklist *mc_lst;
@@ -140,6 +140,8 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
140 struct net *net = sock_net(sk); 140 struct net *net = sock_net(sk);
141 int err; 141 int err;
142 142
143 ASSERT_RTNL();
144
143 if (!ipv6_addr_is_multicast(addr)) 145 if (!ipv6_addr_is_multicast(addr))
144 return -EINVAL; 146 return -EINVAL;
145 147
@@ -161,7 +163,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
161 mc_lst->next = NULL; 163 mc_lst->next = NULL;
162 mc_lst->addr = *addr; 164 mc_lst->addr = *addr;
163 165
164 rtnl_lock();
165 if (ifindex == 0) { 166 if (ifindex == 0) {
166 struct rt6_info *rt; 167 struct rt6_info *rt;
167 rt = rt6_lookup(net, addr, NULL, 0, 0); 168 rt = rt6_lookup(net, addr, NULL, 0, 0);
@@ -173,7 +174,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
173 dev = __dev_get_by_index(net, ifindex); 174 dev = __dev_get_by_index(net, ifindex);
174 175
175 if (dev == NULL) { 176 if (dev == NULL) {
176 rtnl_unlock();
177 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 177 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
178 return -ENODEV; 178 return -ENODEV;
179 } 179 }
@@ -190,7 +190,6 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
190 err = ipv6_dev_mc_inc(dev, addr); 190 err = ipv6_dev_mc_inc(dev, addr);
191 191
192 if (err) { 192 if (err) {
193 rtnl_unlock();
194 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); 193 sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
195 return err; 194 return err;
196 } 195 }
@@ -198,25 +197,37 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
198 mc_lst->next = np->ipv6_mc_list; 197 mc_lst->next = np->ipv6_mc_list;
199 rcu_assign_pointer(np->ipv6_mc_list, mc_lst); 198 rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
200 199
200 return 0;
201}
202EXPORT_SYMBOL(__ipv6_sock_mc_join);
203
204int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
205{
206 int ret;
207
208 rtnl_lock();
209 ret = __ipv6_sock_mc_join(sk, ifindex, addr);
201 rtnl_unlock(); 210 rtnl_unlock();
202 211
203 return 0; 212 return ret;
204} 213}
214EXPORT_SYMBOL(ipv6_sock_mc_join);
205 215
206/* 216/*
207 * socket leave on multicast group 217 * socket leave on multicast group
208 */ 218 */
209int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) 219int __ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
210{ 220{
211 struct ipv6_pinfo *np = inet6_sk(sk); 221 struct ipv6_pinfo *np = inet6_sk(sk);
212 struct ipv6_mc_socklist *mc_lst; 222 struct ipv6_mc_socklist *mc_lst;
213 struct ipv6_mc_socklist __rcu **lnk; 223 struct ipv6_mc_socklist __rcu **lnk;
214 struct net *net = sock_net(sk); 224 struct net *net = sock_net(sk);
215 225
226 ASSERT_RTNL();
227
216 if (!ipv6_addr_is_multicast(addr)) 228 if (!ipv6_addr_is_multicast(addr))
217 return -EINVAL; 229 return -EINVAL;
218 230
219 rtnl_lock();
220 for (lnk = &np->ipv6_mc_list; 231 for (lnk = &np->ipv6_mc_list;
221 (mc_lst = rtnl_dereference(*lnk)) != NULL; 232 (mc_lst = rtnl_dereference(*lnk)) != NULL;
222 lnk = &mc_lst->next) { 233 lnk = &mc_lst->next) {
@@ -235,17 +246,28 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
235 __ipv6_dev_mc_dec(idev, &mc_lst->addr); 246 __ipv6_dev_mc_dec(idev, &mc_lst->addr);
236 } else 247 } else
237 (void) ip6_mc_leave_src(sk, mc_lst, NULL); 248 (void) ip6_mc_leave_src(sk, mc_lst, NULL);
238 rtnl_unlock();
239 249
240 atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); 250 atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
241 kfree_rcu(mc_lst, rcu); 251 kfree_rcu(mc_lst, rcu);
242 return 0; 252 return 0;
243 } 253 }
244 } 254 }
245 rtnl_unlock();
246 255
247 return -EADDRNOTAVAIL; 256 return -EADDRNOTAVAIL;
248} 257}
258EXPORT_SYMBOL(__ipv6_sock_mc_drop);
259
260int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
261{
262 int ret;
263
264 rtnl_lock();
265 ret = __ipv6_sock_mc_drop(sk, ifindex, addr);
266 rtnl_unlock();
267
268 return ret;
269}
270EXPORT_SYMBOL(ipv6_sock_mc_drop);
249 271
250/* called with rcu_read_lock() */ 272/* called with rcu_read_lock() */
251static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, 273static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,
@@ -2907,20 +2929,32 @@ static int __net_init igmp6_net_init(struct net *net)
2907 2929
2908 inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1; 2930 inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;
2909 2931
2932 err = inet_ctl_sock_create(&net->ipv6.mc_autojoin_sk, PF_INET6,
2933 SOCK_RAW, IPPROTO_ICMPV6, net);
2934 if (err < 0) {
2935 pr_err("Failed to initialize the IGMP6 autojoin socket (err %d)\n",
2936 err);
2937 goto out_sock_create;
2938 }
2939
2910 err = igmp6_proc_init(net); 2940 err = igmp6_proc_init(net);
2911 if (err) 2941 if (err)
2912 goto out_sock_create; 2942 goto out_sock_create_autojoin;
2913out:
2914 return err;
2915 2943
2944 return 0;
2945
2946out_sock_create_autojoin:
2947 inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk);
2916out_sock_create: 2948out_sock_create:
2917 inet_ctl_sock_destroy(net->ipv6.igmp_sk); 2949 inet_ctl_sock_destroy(net->ipv6.igmp_sk);
2918 goto out; 2950out:
2951 return err;
2919} 2952}
2920 2953
2921static void __net_exit igmp6_net_exit(struct net *net) 2954static void __net_exit igmp6_net_exit(struct net *net)
2922{ 2955{
2923 inet_ctl_sock_destroy(net->ipv6.igmp_sk); 2956 inet_ctl_sock_destroy(net->ipv6.igmp_sk);
2957 inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk);
2924 igmp6_proc_exit(net); 2958 igmp6_proc_exit(net);
2925} 2959}
2926 2960