lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Mon,  8 Feb 2021 17:58:20 +0000
From:   Taehee Yoo <ap420073@...il.com>
To:     davem@...emloft.net, kuba@...nel.org, netdev@...r.kernel.org,
        dsahern@...nel.org, xiyou.wangcong@...il.com
Cc:     ap420073@...il.com
Subject: [PATCH net-next 7/8] mld: convert ip6_sf_socklist to list macros

Currently, struct ip6_sf_socklist doesn't use list API so that code
shape is a little bit different from others.
So it converts ip6_sf_socklist to use list API so it would
improve readability.

Signed-off-by: Taehee Yoo <ap420073@...il.com>
---
 include/net/if_inet6.h  |  19 +-
 include/uapi/linux/in.h |   4 +-
 net/ipv6/mcast.c        | 387 +++++++++++++++++++++++++---------------
 3 files changed, 256 insertions(+), 154 deletions(-)

diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index babf19c27b29..6885ab8ec2e9 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -13,6 +13,7 @@
 #include <net/snmp.h>
 #include <linux/ipv6.h>
 #include <linux/refcount.h>
+#include <linux/types.h>
 
 /* inet6_dev.if_flags */
 
@@ -76,23 +77,19 @@ struct inet6_ifaddr {
 };
 
 struct ip6_sf_socklist {
-	unsigned int		sl_max;
-	unsigned int		sl_count;
-	struct in6_addr		sl_addr[];
+	struct list_head	list;
+	struct in6_addr		sl_addr;
+	struct rcu_head		rcu;
 };
 
-#define IP6_SFLSIZE(count)	(sizeof(struct ip6_sf_socklist) + \
-	(count) * sizeof(struct in6_addr))
-
-#define IP6_SFBLOCK	10	/* allocate this many at once */
-
 struct ipv6_mc_socklist {
 	struct in6_addr		addr;
 	int			ifindex;
-	unsigned int		sfmode;		/* MCAST_{INCLUDE,EXCLUDE} */
+	bool			sfmode;		/* MCAST_{INCLUDE,EXCLUDE} */
 	struct list_head	list;
+	struct list_head	sflist;
 	rwlock_t		sflock;
-	struct ip6_sf_socklist	*sflist;
+	atomic_t		sl_count;
 	struct rcu_head		rcu;
 };
 
@@ -101,7 +98,7 @@ struct ip6_sf_list {
 	struct in6_addr		sf_addr;
 	unsigned long		sf_count[2];	/* include/exclude counts */
 	unsigned char		sf_gsresp;	/* include in g & s response? */
-	unsigned char		sf_oldin;	/* change state */
+	bool			sf_oldin;	/* change state */
 	unsigned char		sf_crcount;	/* retrans. left to send */
 };
 
diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h
index 7d6687618d80..97024873afd0 100644
--- a/include/uapi/linux/in.h
+++ b/include/uapi/linux/in.h
@@ -160,8 +160,8 @@ struct in_addr {
 #define IP_MULTICAST_ALL		49
 #define IP_UNICAST_IF			50
 
-#define MCAST_EXCLUDE	0
-#define MCAST_INCLUDE	1
+#define MCAST_EXCLUDE	false
+#define MCAST_INCLUDE	true
 
 /* These need to appear somewhere around here */
 #define IP_DEFAULT_MULTICAST_TTL        1
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index f4fc29fcdf48..45b683b15835 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -80,13 +80,18 @@ static int sf_setstate(struct ifmcaddr6 *mc);
 static void sf_markstate(struct ifmcaddr6 *mc);
 static void ip6_mc_clear_src(struct ifmcaddr6 *mc);
 static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *mca,
-			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
-			  int delta);
+			  int sfmode, const struct in6_addr *psfsrc, int delta);
+static void ip6_mc_del_src_bulk(struct inet6_dev *idev,
+				struct ipv6_mc_socklist *mc_lst,
+				struct sock *sk);
 static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *mca,
-			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
-			  int delta);
-static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *mc_lst,
-			    struct inet6_dev *idev);
+			  int sfmode, const struct in6_addr *psfsrc, int delta);
+static int ip6_mc_add_src_bulk(struct inet6_dev *idev, struct group_filter *gsf,
+			       struct list_head *head,
+			       struct sockaddr_storage *list,
+			       struct sock *sk);
+static void ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *mc_lst,
+			     struct inet6_dev *idev);
 static int __ipv6_dev_mc_inc(struct net_device *dev,
 			     const struct in6_addr *addr, unsigned int mode);
 
@@ -178,8 +183,9 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
 
 	mc_lst->ifindex = dev->ifindex;
 	mc_lst->sfmode = mode;
+	atomic_set(&mc_lst->sl_count, 0);
 	rwlock_init(&mc_lst->sflock);
-	mc_lst->sflist = NULL;
+	INIT_LIST_HEAD(&mc_lst->sflist);
 
 	/*
 	 *	now add/increase the group membership on the device
@@ -334,7 +340,6 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
 	int leavegroup = 0;
 	bool found = false;
 	int mclocked = 0;
-	int i, j, rv;
 	int err;
 
 	source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
@@ -365,89 +370,70 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
 		goto done;
 	}
 	/* if a source filter was set, must be the same mode as before */
-	if (mc_lst->sflist) {
+	if (!list_empty(&mc_lst->sflist)) {
 		if (mc_lst->sfmode != omode) {
 			err = -EINVAL;
 			goto done;
 		}
 	} else if (mc_lst->sfmode != omode) {
 		/* allow mode switches for empty-set filters */
-		ip6_mc_add_src(idev, group, omode, 0, NULL, 0);
-		ip6_mc_del_src(idev, group, mc_lst->sfmode, 0, NULL, 0);
+		ip6_mc_add_src(idev, group, omode, NULL, 0);
+		ip6_mc_del_src(idev, group, mc_lst->sfmode, NULL, 0);
 		mc_lst->sfmode = omode;
 	}
 
 	write_lock(&mc_lst->sflock);
 	mclocked = 1;
 
-	psl = mc_lst->sflist;
 	if (!add) {
-		if (!psl)
-			goto done;	/* err = -EADDRNOTAVAIL */
-		rv = !0;
-		for (i = 0; i < psl->sl_count; i++) {
-			rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
-			if (rv == 0)
+		found = false;
+		list_for_each_entry(psl, &mc_lst->sflist, list) {
+			if (ipv6_addr_equal(&psl->sl_addr, source)) {
+				found = true;
 				break;
+			}
 		}
-		if (rv)		/* source not found */
+		if (!found)
 			goto done;	/* err = -EADDRNOTAVAIL */
 
 		/* special case - (INCLUDE, empty) == LEAVE_GROUP */
-		if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {
+		if (atomic_read(&mc_lst->sl_count) == 1 &&
+		    omode == MCAST_INCLUDE) {
 			leavegroup = 1;
 			goto done;
 		}
 
 		/* update the interface filter */
-		ip6_mc_del_src(idev, group, omode, 1, source, 1);
+		ip6_mc_del_src(idev, group, omode, &psl->sl_addr, 1);
 
-		for (j = i+1; j < psl->sl_count; j++)
-			psl->sl_addr[j-1] = psl->sl_addr[j];
-		psl->sl_count--;
+		list_del_rcu(&psl->list);
+		atomic_dec(&mc_lst->sl_count);
 		err = 0;
 		goto done;
 	}
 	/* else, add a new source to the filter */
 
-	if (psl && psl->sl_count >= sysctl_mld_max_msf) {
+	if (atomic_read(&mc_lst->sl_count) >= sysctl_mld_max_msf) {
 		err = -ENOBUFS;
 		goto done;
 	}
-	if (!psl || psl->sl_count == psl->sl_max) {
-		struct ip6_sf_socklist *newpsl;
-		int count = IP6_SFBLOCK;
 
-		if (psl)
-			count += psl->sl_max;
-		newpsl = sock_kmalloc(sk, IP6_SFLSIZE(count), GFP_ATOMIC);
-		if (!newpsl) {
-			err = -ENOBUFS;
-			goto done;
-		}
-		newpsl->sl_max = count;
-		newpsl->sl_count = count - IP6_SFBLOCK;
-		if (psl) {
-			for (i = 0; i < psl->sl_count; i++)
-				newpsl->sl_addr[i] = psl->sl_addr[i];
-			sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max));
-		}
-		psl = newpsl;
-		mc_lst->sflist = psl;
-	}
-	rv = 1;	/* > 0 for insert logic below if sl_count is 0 */
-	for (i = 0; i < psl->sl_count; i++) {
-		rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
-		if (rv == 0) /* There is an error in the address. */
+	list_for_each_entry(psl, &mc_lst->sflist, list)
+		if (ipv6_addr_equal(&psl->sl_addr, source))
 			goto done;
+
+	psl = sock_kmalloc(sk, sizeof(struct ip6_sf_socklist), GFP_ATOMIC);
+	if (!psl) {
+		err = -ENOBUFS;
+		goto done;
 	}
-	for (j = psl->sl_count-1; j >= i; j--)
-		psl->sl_addr[j+1] = psl->sl_addr[j];
-	psl->sl_addr[i] = *source;
-	psl->sl_count++;
+	atomic_inc(&mc_lst->sl_count);
+	psl->sl_addr = *source;
+	list_add_rcu(&psl->list, &mc_lst->sflist);
+
 	err = 0;
 	/* update the interface list */
-	ip6_mc_add_src(idev, group, omode, 1, source, 1);
+	ip6_mc_add_src(idev, group, omode, &psl->sl_addr, 1);
 done:
 	if (mclocked)
 		write_unlock(&mc_lst->sflock);
@@ -462,14 +448,14 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
 		    struct sockaddr_storage *list)
 {
 	struct ipv6_pinfo *inet6 = inet6_sk(sk);
-	struct ip6_sf_socklist *newpsl, *psl;
 	struct ipv6_mc_socklist *mc_lst;
 	struct net *net = sock_net(sk);
 	const struct in6_addr *group;
 	struct inet6_dev *idev;
 	int leavegroup = 0;
 	bool found = false;
-	int i, err;
+	LIST_HEAD(head);
+	int err;
 
 	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
 
@@ -506,40 +492,19 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
 		err = -EINVAL;
 		goto done;
 	}
-	if (gsf->gf_numsrc) {
-		newpsl = sock_kmalloc(sk, IP6_SFLSIZE(gsf->gf_numsrc),
-							  GFP_ATOMIC);
-		if (!newpsl) {
-			err = -ENOBUFS;
-			goto done;
-		}
-		newpsl->sl_max = newpsl->sl_count = gsf->gf_numsrc;
-		for (i = 0; i < newpsl->sl_count; ++i, ++list) {
-			struct sockaddr_in6 *psin6;
 
-			psin6 = (struct sockaddr_in6 *)list;
-			newpsl->sl_addr[i] = psin6->sin6_addr;
-		}
-		err = ip6_mc_add_src(idev, group, gsf->gf_fmode,
-			newpsl->sl_count, newpsl->sl_addr, 0);
-		if (err) {
-			sock_kfree_s(sk, newpsl, IP6_SFLSIZE(newpsl->sl_max));
-			goto done;
-		}
-	} else {
-		newpsl = NULL;
-		ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0);
-	}
+	if (gsf->gf_numsrc)
+		err = ip6_mc_add_src_bulk(idev, gsf, &head, list, sk);
+	else
+		err = ip6_mc_add_src(idev, group, gsf->gf_fmode, NULL, 0);
+
+	if (err)
+		goto done;
 
 	write_lock(&mc_lst->sflock);
-	psl = mc_lst->sflist;
-	if (psl) {
-		ip6_mc_del_src(idev, group, mc_lst->sfmode,
-			       psl->sl_count, psl->sl_addr, 0);
-		sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max));
-	} else
-		ip6_mc_del_src(idev, group, mc_lst->sfmode, 0, NULL, 0);
-	mc_lst->sflist = newpsl;
+	ip6_mc_del_src_bulk(idev, mc_lst, sk);
+	atomic_set(&mc_lst->sl_count, gsf->gf_numsrc);
+	list_splice(&head, &mc_lst->sflist);
 	mc_lst->sfmode = gsf->gf_fmode;
 	write_unlock(&mc_lst->sflock);
 	err = 0;
@@ -548,6 +513,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf,
 	rcu_read_unlock();
 	if (leavegroup)
 		err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group);
+
 	return err;
 }
 
@@ -557,11 +523,11 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
 	struct ipv6_pinfo *inet6 = inet6_sk(sk);
 	struct ipv6_mc_socklist *mc_lst;
 	struct net *net = sock_net(sk);
-	int err, i, count, copycount;
 	const struct in6_addr *group;
 	struct ip6_sf_socklist *psl;
 	struct inet6_dev *idev;
 	bool found = false;
+	int err, i;
 
 	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
 
@@ -593,27 +559,31 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
 	if (!found)		/* must have a prior join */
 		goto done;
 	gsf->gf_fmode = mc_lst->sfmode;
-	psl = mc_lst->sflist;
-	count = psl ? psl->sl_count : 0;
 	read_unlock_bh(&idev->lock);
 	rcu_read_unlock();
 
-	copycount = count < gsf->gf_numsrc ? count : gsf->gf_numsrc;
-	gsf->gf_numsrc = count;
-	/* changes to psl require the socket lock, and a write lock
-	 * on mc_lst->sflock. We have the socket lock so reading here is safe.
-	 */
-	for (i = 0; i < copycount; i++, p++) {
+	i = 0;
+	read_lock(&mc_lst->sflock);
+	list_for_each_entry(psl, &mc_lst->sflist, list) {
 		struct sockaddr_in6 *psin6;
 		struct sockaddr_storage ss;
 
+		if (i >= gsf->gf_numsrc)
+			break;
+
 		psin6 = (struct sockaddr_in6 *)&ss;
 		memset(&ss, 0, sizeof(ss));
 		psin6->sin6_family = AF_INET6;
-		psin6->sin6_addr = psl->sl_addr[i];
-		if (copy_to_user(p, &ss, sizeof(ss)))
+		psin6->sin6_addr = psl->sl_addr;
+		if (copy_to_user(p, &ss, sizeof(ss))) {
+			read_unlock(&mc_lst->sflock);
 			return -EFAULT;
+		}
+		p++;
+		i++;
 	}
+	gsf->gf_numsrc = i;
+	read_unlock(&mc_lst->sflock);
 	return 0;
 done:
 	read_unlock_bh(&idev->lock);
@@ -641,19 +611,20 @@ bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
 		return np->mc_all;
 	}
 	read_lock(&mc_lst->sflock);
-	psl = mc_lst->sflist;
-	if (!psl) {
+
+	found = false;
+	if (list_empty(&mc_lst->sflist)) {
 		rv = mc_lst->sfmode == MCAST_EXCLUDE;
 	} else {
-		int i;
-
-		for (i = 0; i < psl->sl_count; i++) {
-			if (ipv6_addr_equal(&psl->sl_addr[i], src_addr))
+		list_for_each_entry_rcu(psl, &mc_lst->sflist, list) {
+			if (ipv6_addr_equal(&psl->sl_addr, src_addr)) {
+				found = true;
 				break;
+			}
 		}
-		if (mc_lst->sfmode == MCAST_INCLUDE && i >= psl->sl_count)
+		if (mc_lst->sfmode == MCAST_INCLUDE && !found)
 			rv = false;
-		if (mc_lst->sfmode == MCAST_EXCLUDE && i < psl->sl_count)
+		if (mc_lst->sfmode == MCAST_EXCLUDE && found)
 			rv = false;
 	}
 	read_unlock(&mc_lst->sflock);
@@ -900,7 +871,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,
 		if (ipv6_addr_equal(&mc->mca_addr, addr)) {
 			mc->mca_users++;
 			write_unlock_bh(&idev->lock);
-			ip6_mc_add_src(idev, &mc->mca_addr, mode, 0, NULL, 0);
+			ip6_mc_add_src(idev, &mc->mca_addr, mode, NULL, 0);
 			in6_dev_put(idev);
 			return 0;
 		}
@@ -2171,16 +2142,16 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *mc, int sfmode,
 }
 
 static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *mca,
-			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
-			  int delta)
+			  int sfmode, const struct in6_addr *psfsrc, int delta)
 {
 	struct ifmcaddr6 *mc;
 	bool found = false;
 	int changerec = 0;
-	int i, err;
+	int i, err, rv;
 
 	if (!idev)
 		return -ENODEV;
+
 	read_lock_bh(&idev->lock);
 	list_for_each_entry(mc, &idev->mc_list, list) {
 		if (ipv6_addr_equal(mca, &mc->mca_addr)) {
@@ -2204,13 +2175,16 @@ static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *mca,
 		mc->mca_sfcount[sfmode]--;
 	}
 	err = 0;
-	for (i = 0; i < sfcount; i++) {
-		int rv = ip6_mc_del1_src(mc, sfmode, &psfsrc[i]);
+	i = 0;
+
+	if (psfsrc) {
+		rv = ip6_mc_del1_src(mc, sfmode, psfsrc);
 
 		changerec |= rv > 0;
 		if (!err && rv < 0)
 			err = rv;
 	}
+
 	if (mc->mca_sfmode == MCAST_EXCLUDE &&
 	    mc->mca_sfcount[MCAST_EXCLUDE] == 0 &&
 	    mc->mca_sfcount[MCAST_INCLUDE]) {
@@ -2231,6 +2205,71 @@ static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *mca,
 	return err;
 }
 
+static void ip6_mc_del_src_bulk(struct inet6_dev *idev,
+				struct ipv6_mc_socklist *mc_lst,
+				struct sock *sk)
+{
+	struct in6_addr *mca = &mc_lst->addr;
+	struct ip6_sf_socklist *psl, *tmp;
+	int sfmode = mc_lst->sfmode;
+	struct ifmcaddr6 *mc;
+	bool found = false;
+	int changerec = 0;
+	int i, rv;
+
+	if (!idev)
+		return;
+
+	read_lock_bh(&idev->lock);
+	list_for_each_entry(mc, &idev->mc_list, list) {
+		if (ipv6_addr_equal(mca, &mc->mca_addr)) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		/* MCA not found?? bug */
+		read_unlock_bh(&idev->lock);
+		return;
+	}
+	spin_lock_bh(&mc->mca_lock);
+	sf_markstate(mc);
+	if (!mc->mca_sfcount[sfmode]) {
+		spin_unlock_bh(&mc->mca_lock);
+		read_unlock_bh(&idev->lock);
+		return;
+	}
+	mc->mca_sfcount[sfmode]--;
+	i = 0;
+
+	list_for_each_entry_safe(psl, tmp, &mc_lst->sflist, list) {
+		rv = ip6_mc_del1_src(mc, sfmode, &psl->sl_addr);
+		list_del_rcu(&psl->list);
+		atomic_sub(sizeof(*psl), &sk->sk_omem_alloc);
+		kfree_rcu(psl, rcu);
+
+		changerec |= rv > 0;
+	}
+
+	if (mc->mca_sfmode == MCAST_EXCLUDE &&
+	    mc->mca_sfcount[MCAST_EXCLUDE] == 0 &&
+	    mc->mca_sfcount[MCAST_INCLUDE]) {
+		struct ip6_sf_list *psf;
+
+		/* filter mode change */
+		mc->mca_sfmode = MCAST_INCLUDE;
+		mc->mca_crcount = idev->mc_qrv;
+		idev->mc_ifc_count = mc->mca_crcount;
+		list_for_each_entry(psf, &mc->mca_source_list, list)
+			psf->sf_crcount = 0;
+		mld_ifc_event(mc->idev);
+	} else if (sf_setstate(mc) || changerec) {
+		mld_ifc_event(mc->idev);
+	}
+	spin_unlock_bh(&mc->mca_lock);
+	read_unlock_bh(&idev->lock);
+}
+
 /*
  * Add multicast single-source filter to the interface list
  */
@@ -2353,14 +2392,14 @@ static int sf_setstate(struct ifmcaddr6 *mc)
 /*
  * Add multicast source filter list to the interface list
  */
+
 static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *mca,
-			  int sfmode, int sfcount, const struct in6_addr *psfsrc,
-			  int delta)
+			  int sfmode, const struct in6_addr *psfsrc, int delta)
 {
 	struct ifmcaddr6 *mc;
 	bool found = false;
 	int isexclude;
-	int i, err;
+	int err = 0;
 
 	if (!idev)
 		return -ENODEV;
@@ -2383,19 +2422,99 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *mca,
 	isexclude = mc->mca_sfmode == MCAST_EXCLUDE;
 	if (!delta)
 		mc->mca_sfcount[sfmode]++;
-	err = 0;
-	for (i = 0; i < sfcount; i++) {
-		err = ip6_mc_add1_src(mc, sfmode, &psfsrc[i]);
+
+	if (psfsrc)
+		err = ip6_mc_add1_src(mc, sfmode, psfsrc);
+
+	if (err) {
+		if (!delta)
+			mc->mca_sfcount[sfmode]--;
+	} else if (isexclude != (mc->mca_sfcount[MCAST_EXCLUDE] != 0)) {
+		struct ip6_sf_list *psf;
+
+		/* filter mode change */
+		if (mc->mca_sfcount[MCAST_EXCLUDE])
+			mc->mca_sfmode = MCAST_EXCLUDE;
+		else if (mc->mca_sfcount[MCAST_INCLUDE])
+			mc->mca_sfmode = MCAST_INCLUDE;
+		/* else no filters; keep old mode for reports */
+
+		mc->mca_crcount = idev->mc_qrv;
+		idev->mc_ifc_count = mc->mca_crcount;
+		list_for_each_entry(psf, &mc->mca_source_list, list)
+			psf->sf_crcount = 0;
+		mld_ifc_event(idev);
+	} else if (sf_setstate(mc)) {
+		mld_ifc_event(idev);
+	}
+
+	spin_unlock_bh(&mc->mca_lock);
+	read_unlock_bh(&idev->lock);
+	return err;
+}
+
+static int ip6_mc_add_src_bulk(struct inet6_dev *idev, struct group_filter *gsf,
+			       struct list_head *head,
+			       struct sockaddr_storage *list,
+			       struct sock *sk)
+{
+	struct ip6_sf_socklist *psl, *tmp;
+	const struct in6_addr *group;
+	int sfmode = gsf->gf_fmode;
+	struct ifmcaddr6 *mc;
+	bool found = false;
+	int isexclude;
+	int i, err = 0;
+
+	group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
+
+	if (!idev)
+		return -ENODEV;
+
+	list_for_each_entry(mc, &idev->mc_list, list) {
+		if (ipv6_addr_equal(group, &mc->mca_addr)) {
+			found = true;
+			break;
+		}
+	}
+	if (!found) {
+		/* MCA not found?? bug */
+		return -ESRCH;
+	}
+	spin_lock_bh(&mc->mca_lock);
+
+	sf_markstate(mc);
+	isexclude = mc->mca_sfmode == MCAST_EXCLUDE;
+	mc->mca_sfcount[sfmode]++;
+
+	for (i = 0; i < gsf->gf_numsrc; i++, ++list) {
+		struct sockaddr_in6 *psin6;
+
+		psl = sock_kmalloc(sk, sizeof(struct ip6_sf_socklist),
+				   GFP_ATOMIC);
+		if (!psl) {
+			err = -ENOBUFS;
+			break;
+		}
+		INIT_LIST_HEAD(&psl->list);
+		psin6 = (struct sockaddr_in6 *)list;
+		psl->sl_addr = psin6->sin6_addr;
+
+		err = ip6_mc_add1_src(mc, gsf->gf_fmode, &psl->sl_addr);
 		if (err)
 			break;
+
+		list_add_tail(&psl->list, head);
 	}
+
 	if (err) {
-		int j;
+		mc->mca_sfcount[sfmode]--;
 
-		if (!delta)
-			mc->mca_sfcount[sfmode]--;
-		for (j = 0; j < i; j++)
-			ip6_mc_del1_src(mc, sfmode, &psfsrc[j]);
+		list_for_each_entry_safe(psl, tmp, head, list) {
+			list_del(&psl->list);
+			atomic_sub(sizeof(*psl), &sk->sk_omem_alloc);
+			kfree(psl);
+		}
 	} else if (isexclude != (mc->mca_sfcount[MCAST_EXCLUDE] != 0)) {
 		struct ip6_sf_list *psf;
 
@@ -2414,7 +2533,7 @@ static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *mca,
 	} else if (sf_setstate(mc))
 		mld_ifc_event(idev);
 	spin_unlock_bh(&mc->mca_lock);
-	read_unlock_bh(&idev->lock);
+
 	return err;
 }
 
@@ -2462,26 +2581,12 @@ static void mld_join_group(struct ifmcaddr6 *mc)
 	spin_unlock_bh(&mc->mca_lock);
 }
 
-static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *mc_lst,
-			    struct inet6_dev *idev)
+static void ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *mc_lst,
+			     struct inet6_dev *idev)
 {
-	int err;
-
 	write_lock_bh(&mc_lst->sflock);
-	if (!mc_lst->sflist) {
-		/* any-source empty exclude case */
-		err = ip6_mc_del_src(idev, &mc_lst->addr, mc_lst->sfmode,
-				     0, NULL, 0);
-	} else {
-		err = ip6_mc_del_src(idev, &mc_lst->addr, mc_lst->sfmode,
-				     mc_lst->sflist->sl_count,
-				     mc_lst->sflist->sl_addr, 0);
-		sock_kfree_s(sk, mc_lst->sflist,
-			     IP6_SFLSIZE(mc_lst->sflist->sl_max));
-		mc_lst->sflist = NULL;
-	}
+	ip6_mc_del_src_bulk(idev, mc_lst, sk);
 	write_unlock_bh(&mc_lst->sflock);
-	return err;
 }
 
 static void mld_leave_group(struct ifmcaddr6 *mc)
-- 
2.17.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ