|
@@ -95,6 +95,8 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,
|
|
int delta);
|
|
int delta);
|
|
static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
|
|
static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
|
|
struct inet6_dev *idev);
|
|
struct inet6_dev *idev);
|
|
|
|
+static int __ipv6_dev_mc_inc(struct net_device *dev,
|
|
|
|
+ const struct in6_addr *addr, unsigned int mode);
|
|
|
|
|
|
#define MLD_QRV_DEFAULT 2
|
|
#define MLD_QRV_DEFAULT 2
|
|
/* RFC3810, 9.2. Query Interval */
|
|
/* RFC3810, 9.2. Query Interval */
|
|
@@ -132,7 +134,8 @@ static int unsolicited_report_interval(struct inet6_dev *idev)
|
|
return iv > 0 ? iv : 1;
|
|
return iv > 0 ? iv : 1;
|
|
}
|
|
}
|
|
|
|
|
|
-int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
|
|
|
|
|
|
+static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
|
|
|
|
+ const struct in6_addr *addr, unsigned int mode)
|
|
{
|
|
{
|
|
struct net_device *dev = NULL;
|
|
struct net_device *dev = NULL;
|
|
struct ipv6_mc_socklist *mc_lst;
|
|
struct ipv6_mc_socklist *mc_lst;
|
|
@@ -179,7 +182,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
|
|
}
|
|
}
|
|
|
|
|
|
mc_lst->ifindex = dev->ifindex;
|
|
mc_lst->ifindex = dev->ifindex;
|
|
- mc_lst->sfmode = MCAST_EXCLUDE;
|
|
|
|
|
|
+ mc_lst->sfmode = mode;
|
|
rwlock_init(&mc_lst->sflock);
|
|
rwlock_init(&mc_lst->sflock);
|
|
mc_lst->sflist = NULL;
|
|
mc_lst->sflist = NULL;
|
|
|
|
|
|
@@ -187,7 +190,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
|
|
* now add/increase the group membership on the device
|
|
* now add/increase the group membership on the device
|
|
*/
|
|
*/
|
|
|
|
|
|
- err = ipv6_dev_mc_inc(dev, addr);
|
|
|
|
|
|
+ err = __ipv6_dev_mc_inc(dev, addr, mode);
|
|
|
|
|
|
if (err) {
|
|
if (err) {
|
|
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
|
|
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
|
|
@@ -199,8 +202,19 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
|
|
|
|
+{
|
|
|
|
+ return __ipv6_sock_mc_join(sk, ifindex, addr, MCAST_EXCLUDE);
|
|
|
|
+}
|
|
EXPORT_SYMBOL(ipv6_sock_mc_join);
|
|
EXPORT_SYMBOL(ipv6_sock_mc_join);
|
|
|
|
|
|
|
|
+int ipv6_sock_mc_join_ssm(struct sock *sk, int ifindex,
|
|
|
|
+ const struct in6_addr *addr, unsigned int mode)
|
|
|
|
+{
|
|
|
|
+ return __ipv6_sock_mc_join(sk, ifindex, addr, mode);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* socket leave on multicast group
|
|
* socket leave on multicast group
|
|
*/
|
|
*/
|
|
@@ -646,7 +660,7 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
|
|
return rv;
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
|
|
-static void igmp6_group_added(struct ifmcaddr6 *mc)
|
|
|
|
|
|
+static void igmp6_group_added(struct ifmcaddr6 *mc, unsigned int mode)
|
|
{
|
|
{
|
|
struct net_device *dev = mc->idev->dev;
|
|
struct net_device *dev = mc->idev->dev;
|
|
char buf[MAX_ADDR_LEN];
|
|
char buf[MAX_ADDR_LEN];
|
|
@@ -672,7 +686,13 @@ static void igmp6_group_added(struct ifmcaddr6 *mc)
|
|
}
|
|
}
|
|
/* else v2 */
|
|
/* else v2 */
|
|
|
|
|
|
- mc->mca_crcount = mc->idev->mc_qrv;
|
|
|
|
|
|
+ /* Based on RFC3810 6.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)
|
|
|
|
+ mc->mca_crcount = mc->idev->mc_qrv;
|
|
|
|
+
|
|
mld_ifc_event(mc->idev);
|
|
mld_ifc_event(mc->idev);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -770,13 +790,14 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
|
|
spin_lock_bh(&im->mca_lock);
|
|
spin_lock_bh(&im->mca_lock);
|
|
if (pmc) {
|
|
if (pmc) {
|
|
im->idev = pmc->idev;
|
|
im->idev = pmc->idev;
|
|
- im->mca_crcount = idev->mc_qrv;
|
|
|
|
im->mca_sfmode = pmc->mca_sfmode;
|
|
im->mca_sfmode = pmc->mca_sfmode;
|
|
if (pmc->mca_sfmode == MCAST_INCLUDE) {
|
|
if (pmc->mca_sfmode == MCAST_INCLUDE) {
|
|
im->mca_tomb = pmc->mca_tomb;
|
|
im->mca_tomb = pmc->mca_tomb;
|
|
im->mca_sources = pmc->mca_sources;
|
|
im->mca_sources = pmc->mca_sources;
|
|
for (psf = im->mca_sources; psf; psf = psf->sf_next)
|
|
for (psf = im->mca_sources; psf; psf = psf->sf_next)
|
|
- psf->sf_crcount = im->mca_crcount;
|
|
|
|
|
|
+ psf->sf_crcount = idev->mc_qrv;
|
|
|
|
+ } else {
|
|
|
|
+ im->mca_crcount = idev->mc_qrv;
|
|
}
|
|
}
|
|
in6_dev_put(pmc->idev);
|
|
in6_dev_put(pmc->idev);
|
|
kfree(pmc);
|
|
kfree(pmc);
|
|
@@ -831,7 +852,8 @@ static void ma_put(struct ifmcaddr6 *mc)
|
|
}
|
|
}
|
|
|
|
|
|
static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
|
|
static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
|
|
- const struct in6_addr *addr)
|
|
|
|
|
|
+ const struct in6_addr *addr,
|
|
|
|
+ unsigned int mode)
|
|
{
|
|
{
|
|
struct ifmcaddr6 *mc;
|
|
struct ifmcaddr6 *mc;
|
|
|
|
|
|
@@ -849,9 +871,8 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
|
|
refcount_set(&mc->mca_refcnt, 1);
|
|
refcount_set(&mc->mca_refcnt, 1);
|
|
spin_lock_init(&mc->mca_lock);
|
|
spin_lock_init(&mc->mca_lock);
|
|
|
|
|
|
- /* initial mode is (EX, empty) */
|
|
|
|
- mc->mca_sfmode = MCAST_EXCLUDE;
|
|
|
|
- mc->mca_sfcount[MCAST_EXCLUDE] = 1;
|
|
|
|
|
|
+ mc->mca_sfmode = mode;
|
|
|
|
+ mc->mca_sfcount[mode] = 1;
|
|
|
|
|
|
if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) ||
|
|
if (ipv6_addr_is_ll_all_nodes(&mc->mca_addr) ||
|
|
IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL)
|
|
IPV6_ADDR_MC_SCOPE(&mc->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL)
|
|
@@ -863,7 +884,8 @@ static struct ifmcaddr6 *mca_alloc(struct inet6_dev *idev,
|
|
/*
|
|
/*
|
|
* device multicast group inc (add if not found)
|
|
* device multicast group inc (add if not found)
|
|
*/
|
|
*/
|
|
-int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
|
|
|
|
|
|
+static int __ipv6_dev_mc_inc(struct net_device *dev,
|
|
|
|
+ const struct in6_addr *addr, unsigned int mode)
|
|
{
|
|
{
|
|
struct ifmcaddr6 *mc;
|
|
struct ifmcaddr6 *mc;
|
|
struct inet6_dev *idev;
|
|
struct inet6_dev *idev;
|
|
@@ -887,14 +909,13 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
|
|
if (ipv6_addr_equal(&mc->mca_addr, addr)) {
|
|
if (ipv6_addr_equal(&mc->mca_addr, addr)) {
|
|
mc->mca_users++;
|
|
mc->mca_users++;
|
|
write_unlock_bh(&idev->lock);
|
|
write_unlock_bh(&idev->lock);
|
|
- ip6_mc_add_src(idev, &mc->mca_addr, MCAST_EXCLUDE, 0,
|
|
|
|
- NULL, 0);
|
|
|
|
|
|
+ ip6_mc_add_src(idev, &mc->mca_addr, mode, 0, NULL, 0);
|
|
in6_dev_put(idev);
|
|
in6_dev_put(idev);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- mc = mca_alloc(idev, addr);
|
|
|
|
|
|
+ mc = mca_alloc(idev, addr, mode);
|
|
if (!mc) {
|
|
if (!mc) {
|
|
write_unlock_bh(&idev->lock);
|
|
write_unlock_bh(&idev->lock);
|
|
in6_dev_put(idev);
|
|
in6_dev_put(idev);
|
|
@@ -911,11 +932,16 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
|
|
write_unlock_bh(&idev->lock);
|
|
write_unlock_bh(&idev->lock);
|
|
|
|
|
|
mld_del_delrec(idev, mc);
|
|
mld_del_delrec(idev, mc);
|
|
- igmp6_group_added(mc);
|
|
|
|
|
|
+ igmp6_group_added(mc, mode);
|
|
ma_put(mc);
|
|
ma_put(mc);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
|
|
|
|
+{
|
|
|
|
+ return __ipv6_dev_mc_inc(dev, addr, MCAST_EXCLUDE);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* device multicast group del
|
|
* device multicast group del
|
|
*/
|
|
*/
|
|
@@ -1751,7 +1777,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
|
|
|
|
|
|
psf_next = psf->sf_next;
|
|
psf_next = psf->sf_next;
|
|
|
|
|
|
- if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
|
|
|
|
|
|
+ if (!is_in(pmc, psf, type, gdeleted, sdeleted) && !crsend) {
|
|
psf_prev = psf;
|
|
psf_prev = psf;
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
@@ -2066,7 +2092,7 @@ static void mld_send_initial_cr(struct inet6_dev *idev)
|
|
if (pmc->mca_sfcount[MCAST_EXCLUDE])
|
|
if (pmc->mca_sfcount[MCAST_EXCLUDE])
|
|
type = MLD2_CHANGE_TO_EXCLUDE;
|
|
type = MLD2_CHANGE_TO_EXCLUDE;
|
|
else
|
|
else
|
|
- type = MLD2_CHANGE_TO_INCLUDE;
|
|
|
|
|
|
+ type = MLD2_ALLOW_NEW_SOURCES;
|
|
skb = add_grec(skb, pmc, type, 0, 0, 1);
|
|
skb = add_grec(skb, pmc, type, 0, 0, 1);
|
|
spin_unlock_bh(&pmc->mca_lock);
|
|
spin_unlock_bh(&pmc->mca_lock);
|
|
}
|
|
}
|
|
@@ -2546,7 +2572,7 @@ void ipv6_mc_up(struct inet6_dev *idev)
|
|
ipv6_mc_reset(idev);
|
|
ipv6_mc_reset(idev);
|
|
for (i = idev->mc_list; i; i = i->next) {
|
|
for (i = idev->mc_list; i; i = i->next) {
|
|
mld_del_delrec(idev, i);
|
|
mld_del_delrec(idev, i);
|
|
- igmp6_group_added(i);
|
|
|
|
|
|
+ igmp6_group_added(i, i->mca_sfmode);
|
|
}
|
|
}
|
|
read_unlock_bh(&idev->lock);
|
|
read_unlock_bh(&idev->lock);
|
|
}
|
|
}
|