aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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.c20
7 files changed, 98 insertions, 7 deletions
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 e4955d019734..1dd1fedff9f4 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -2929,20 +2929,32 @@ static int __net_init igmp6_net_init(struct net *net)
2929 2929
2930 inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1; 2930 inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;
2931 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
2932 err = igmp6_proc_init(net); 2940 err = igmp6_proc_init(net);
2933 if (err) 2941 if (err)
2934 goto out_sock_create; 2942 goto out_sock_create_autojoin;
2935out: 2943
2936 return err; 2944 return 0;
2937 2945
2946out_sock_create_autojoin:
2947 inet_ctl_sock_destroy(net->ipv6.mc_autojoin_sk);
2938out_sock_create: 2948out_sock_create:
2939 inet_ctl_sock_destroy(net->ipv6.igmp_sk); 2949 inet_ctl_sock_destroy(net->ipv6.igmp_sk);
2940 goto out; 2950out:
2951 return err;
2941} 2952}
2942 2953
2943static void __net_exit igmp6_net_exit(struct net *net) 2954static void __net_exit igmp6_net_exit(struct net *net)
2944{ 2955{
2945 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);
2946 igmp6_proc_exit(net); 2958 igmp6_proc_exit(net);
2947} 2959}
2948 2960