|
@@ -1200,13 +1200,14 @@ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
|
|
spin_lock_bh(&im->lock);
|
|
spin_lock_bh(&im->lock);
|
|
if (pmc) {
|
|
if (pmc) {
|
|
im->interface = pmc->interface;
|
|
im->interface = pmc->interface;
|
|
- im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
|
|
|
|
im->sfmode = pmc->sfmode;
|
|
im->sfmode = pmc->sfmode;
|
|
if (pmc->sfmode == MCAST_INCLUDE) {
|
|
if (pmc->sfmode == MCAST_INCLUDE) {
|
|
im->tomb = pmc->tomb;
|
|
im->tomb = pmc->tomb;
|
|
im->sources = pmc->sources;
|
|
im->sources = pmc->sources;
|
|
for (psf = im->sources; psf; psf = psf->sf_next)
|
|
for (psf = im->sources; psf; psf = psf->sf_next)
|
|
- psf->sf_crcount = im->crcount;
|
|
|
|
|
|
+ psf->sf_crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
|
|
|
|
+ } else {
|
|
|
|
+ im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
|
|
}
|
|
}
|
|
in_dev_put(pmc->interface);
|
|
in_dev_put(pmc->interface);
|
|
kfree(pmc);
|
|
kfree(pmc);
|
|
@@ -1288,7 +1289,7 @@ static void igmp_group_dropped(struct ip_mc_list *im)
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
-static void igmp_group_added(struct ip_mc_list *im)
|
|
|
|
|
|
+static void igmp_group_added(struct ip_mc_list *im, unsigned int mode)
|
|
{
|
|
{
|
|
struct in_device *in_dev = im->interface;
|
|
struct in_device *in_dev = im->interface;
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
@@ -1316,7 +1317,13 @@ static void igmp_group_added(struct ip_mc_list *im)
|
|
}
|
|
}
|
|
/* else, v3 */
|
|
/* else, v3 */
|
|
|
|
|
|
- im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
|
|
|
|
|
|
+ /* Based on RFC3376 5.1, for newly added INCLUDE SSM, we should
|
|
|
|
+ * not send filter-mode change record as the mode should be from
|
|
|
|
+ * IN() to IN(A).
|
|
|
|
+ */
|
|
|
|
+ if (mode == MCAST_EXCLUDE)
|
|
|
|
+ im->crcount = in_dev->mr_qrv ?: net->ipv4.sysctl_igmp_qrv;
|
|
|
|
+
|
|
igmp_ifc_event(in_dev);
|
|
igmp_ifc_event(in_dev);
|
|
#endif
|
|
#endif
|
|
}
|
|
}
|
|
@@ -1381,8 +1388,7 @@ static void ip_mc_hash_remove(struct in_device *in_dev,
|
|
/*
|
|
/*
|
|
* A socket has joined a multicast group on device dev.
|
|
* A socket has joined a multicast group on device dev.
|
|
*/
|
|
*/
|
|
-
|
|
|
|
-void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
|
|
|
|
|
|
+void __ip_mc_inc_group(struct in_device *in_dev, __be32 addr, unsigned int mode)
|
|
{
|
|
{
|
|
struct ip_mc_list *im;
|
|
struct ip_mc_list *im;
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
@@ -1394,7 +1400,7 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
|
|
for_each_pmc_rtnl(in_dev, im) {
|
|
for_each_pmc_rtnl(in_dev, im) {
|
|
if (im->multiaddr == addr) {
|
|
if (im->multiaddr == addr) {
|
|
im->users++;
|
|
im->users++;
|
|
- ip_mc_add_src(in_dev, &addr, MCAST_EXCLUDE, 0, NULL, 0);
|
|
|
|
|
|
+ ip_mc_add_src(in_dev, &addr, mode, 0, NULL, 0);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1408,8 +1414,8 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
|
|
in_dev_hold(in_dev);
|
|
in_dev_hold(in_dev);
|
|
im->multiaddr = addr;
|
|
im->multiaddr = addr;
|
|
/* initial mode is (EX, empty) */
|
|
/* initial mode is (EX, empty) */
|
|
- im->sfmode = MCAST_EXCLUDE;
|
|
|
|
- im->sfcount[MCAST_EXCLUDE] = 1;
|
|
|
|
|
|
+ im->sfmode = mode;
|
|
|
|
+ im->sfcount[mode] = 1;
|
|
refcount_set(&im->refcnt, 1);
|
|
refcount_set(&im->refcnt, 1);
|
|
spin_lock_init(&im->lock);
|
|
spin_lock_init(&im->lock);
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
@@ -1426,12 +1432,17 @@ void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
igmpv3_del_delrec(in_dev, im);
|
|
igmpv3_del_delrec(in_dev, im);
|
|
#endif
|
|
#endif
|
|
- igmp_group_added(im);
|
|
|
|
|
|
+ igmp_group_added(im, mode);
|
|
if (!in_dev->dead)
|
|
if (!in_dev->dead)
|
|
ip_rt_multicast_event(in_dev);
|
|
ip_rt_multicast_event(in_dev);
|
|
out:
|
|
out:
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+void ip_mc_inc_group(struct in_device *in_dev, __be32 addr)
|
|
|
|
+{
|
|
|
|
+ __ip_mc_inc_group(in_dev, addr, MCAST_EXCLUDE);
|
|
|
|
+}
|
|
EXPORT_SYMBOL(ip_mc_inc_group);
|
|
EXPORT_SYMBOL(ip_mc_inc_group);
|
|
|
|
|
|
static int ip_mc_check_iphdr(struct sk_buff *skb)
|
|
static int ip_mc_check_iphdr(struct sk_buff *skb)
|
|
@@ -1688,7 +1699,7 @@ void ip_mc_remap(struct in_device *in_dev)
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
igmpv3_del_delrec(in_dev, pmc);
|
|
igmpv3_del_delrec(in_dev, pmc);
|
|
#endif
|
|
#endif
|
|
- igmp_group_added(pmc);
|
|
|
|
|
|
+ igmp_group_added(pmc, pmc->sfmode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1751,7 +1762,7 @@ void ip_mc_up(struct in_device *in_dev)
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
#ifdef CONFIG_IP_MULTICAST
|
|
igmpv3_del_delrec(in_dev, pmc);
|
|
igmpv3_del_delrec(in_dev, pmc);
|
|
#endif
|
|
#endif
|
|
- igmp_group_added(pmc);
|
|
|
|
|
|
+ igmp_group_added(pmc, pmc->sfmode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2130,8 +2141,8 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc)
|
|
|
|
|
|
/* Join a multicast group
|
|
/* Join a multicast group
|
|
*/
|
|
*/
|
|
-
|
|
|
|
-int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr)
|
|
|
|
|
|
+static int __ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr,
|
|
|
|
+ unsigned int mode)
|
|
{
|
|
{
|
|
__be32 addr = imr->imr_multiaddr.s_addr;
|
|
__be32 addr = imr->imr_multiaddr.s_addr;
|
|
struct ip_mc_socklist *iml, *i;
|
|
struct ip_mc_socklist *iml, *i;
|
|
@@ -2172,15 +2183,30 @@ int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr)
|
|
memcpy(&iml->multi, imr, sizeof(*imr));
|
|
memcpy(&iml->multi, imr, sizeof(*imr));
|
|
iml->next_rcu = inet->mc_list;
|
|
iml->next_rcu = inet->mc_list;
|
|
iml->sflist = NULL;
|
|
iml->sflist = NULL;
|
|
- iml->sfmode = MCAST_EXCLUDE;
|
|
|
|
|
|
+ iml->sfmode = mode;
|
|
rcu_assign_pointer(inet->mc_list, iml);
|
|
rcu_assign_pointer(inet->mc_list, iml);
|
|
- ip_mc_inc_group(in_dev, addr);
|
|
|
|
|
|
+ __ip_mc_inc_group(in_dev, addr, mode);
|
|
err = 0;
|
|
err = 0;
|
|
done:
|
|
done:
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+/* Join ASM (Any-Source Multicast) group
|
|
|
|
+ */
|
|
|
|
+int ip_mc_join_group(struct sock *sk, struct ip_mreqn *imr)
|
|
|
|
+{
|
|
|
|
+ return __ip_mc_join_group(sk, imr, MCAST_EXCLUDE);
|
|
|
|
+}
|
|
EXPORT_SYMBOL(ip_mc_join_group);
|
|
EXPORT_SYMBOL(ip_mc_join_group);
|
|
|
|
|
|
|
|
+/* Join SSM (Source-Specific Multicast) group
|
|
|
|
+ */
|
|
|
|
+int ip_mc_join_group_ssm(struct sock *sk, struct ip_mreqn *imr,
|
|
|
|
+ unsigned int mode)
|
|
|
|
+{
|
|
|
|
+ return __ip_mc_join_group(sk, imr, mode);
|
|
|
|
+}
|
|
|
|
+
|
|
static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
|
|
static int ip_mc_leave_src(struct sock *sk, struct ip_mc_socklist *iml,
|
|
struct in_device *in_dev)
|
|
struct in_device *in_dev)
|
|
{
|
|
{
|