Release 4.11 net/ipv6/mcast.c
/*
* Multicast support for IPv6
* Linux INET6 implementation
*
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
* Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
/* Changes:
*
* yoshfuji : fix format of router-alert option
* YOSHIFUJI Hideaki @USAGI:
* Fixed source address for MLD message based on
* <draft-ietf-magma-mld-source-05.txt>.
* YOSHIFUJI Hideaki @USAGI:
* - Ignore Queries for invalid addresses.
* - MLD for link-local addresses.
* David L Stevens <dlstevens@us.ibm.com>:
* - MLDv2 support
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/jiffies.h>
#include <linux/times.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/route.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/pkt_sched.h>
#include <net/mld.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/if_inet6.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <net/inet_common.h>
#include <net/ip6_checksum.h>
/* Ensure that we have struct in6_addr aligned on 32bit word. */
static void *__mld2_query_bugs[] __attribute__((__unused__)) = {
BUILD_BUG_ON_NULL(offsetof(struct mld2_query, mld2q_srcs) % 4),
BUILD_BUG_ON_NULL(offsetof(struct mld2_report, mld2r_grec) % 4),
BUILD_BUG_ON_NULL(offsetof(struct mld2_grec, grec_mca) % 4)
};
static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
static void igmp6_join_group(struct ifmcaddr6 *ma);
static void igmp6_leave_group(struct ifmcaddr6 *ma);
static void igmp6_timer_handler(unsigned long data);
static void mld_gq_timer_expire(unsigned long data);
static void mld_ifc_timer_expire(unsigned long data);
static void mld_ifc_event(struct inet6_dev *idev);
static void mld_add_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc);
static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *pmc);
static void mld_clear_delrec(struct inet6_dev *idev);
static bool mld_in_v1_mode(const struct inet6_dev *idev);
static int sf_setstate(struct ifmcaddr6 *pmc);
static void sf_markstate(struct ifmcaddr6 *pmc);
static void ip6_mc_clear_src(struct ifmcaddr6 *pmc);
static int ip6_mc_del_src(struct inet6_dev *idev, const struct in6_addr *pmca,
int sfmode, int sfcount, const struct in6_addr *psfsrc,
int delta);
static int ip6_mc_add_src(struct inet6_dev *idev, const struct in6_addr *pmca,
int sfmode, int sfcount, const struct in6_addr *psfsrc,
int delta);
static int ip6_mc_leave_src(struct sock *sk, struct ipv6_mc_socklist *iml,
struct inet6_dev *idev);
#define MLD_QRV_DEFAULT 2
/* RFC3810, 9.2. Query Interval */
#define MLD_QI_DEFAULT (125 * HZ)
/* RFC3810, 9.3. Query Response Interval */
#define MLD_QRI_DEFAULT (10 * HZ)
/* RFC3810, 8.1 Query Version Distinctions */
#define MLD_V1_QUERY_LEN 24
#define MLD_V2_QUERY_LEN_MIN 28
#define IPV6_MLD_MAX_MSF 64
int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF;
int sysctl_mld_qrv __read_mostly = MLD_QRV_DEFAULT;
/*
* socket join on multicast group
*/
#define for_each_pmc_rcu(np, pmc) \
for (pmc = rcu_dereference(np->ipv6_mc_list); \
pmc != NULL; \
pmc = rcu_dereference(pmc->next))
static int unsolicited_report_interval(struct inet6_dev *idev)
{
int iv;
if (mld_in_v1_mode(idev))
iv = idev->cnf.mldv1_unsolicited_report_interval;
else
iv = idev->cnf.mldv2_unsolicited_report_interval;
return iv > 0 ? iv : 1;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Hannes Frederic Sowa | 46 | 97.87% | 1 | 50.00% |
Daniel Borkmann | 1 | 2.13% | 1 | 50.00% |
Total | 47 | 100.00% | 2 | 100.00% |
int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
struct net_device *dev = NULL;
struct ipv6_mc_socklist *mc_lst;
struct ipv6_pinfo *np = inet6_sk(sk);
struct net *net = sock_net(sk);
int err;
ASSERT_RTNL();
if (!ipv6_addr_is_multicast(addr))
return -EINVAL;
rcu_read_lock();
for_each_pmc_rcu(np, mc_lst) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) {
rcu_read_unlock();
return -EADDRINUSE;
}
}
rcu_read_unlock();
mc_lst = sock_kmalloc(sk, sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
if (!mc_lst)
return -ENOMEM;
mc_lst->next = NULL;
mc_lst->addr = *addr;
if (ifindex == 0) {
struct rt6_info *rt;
rt = rt6_lookup(net, addr, NULL, 0, 0);
if (rt) {
dev = rt->dst.dev;
ip6_rt_put(rt);
}
} else
dev = __dev_get_by_index(net, ifindex);
if (!dev) {
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return -ENODEV;
}
mc_lst->ifindex = dev->ifindex;
mc_lst->sfmode = MCAST_EXCLUDE;
rwlock_init(&mc_lst->sflock);
mc_lst->sflist = NULL;
/*
* now add/increase the group membership on the device
*/
err = ipv6_dev_mc_inc(dev, addr);
if (err) {
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return err;
}
mc_lst->next = np->ipv6_mc_list;
rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
return 0;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 160 | 50.96% | 4 | 16.67% |
David L Stevens | 93 | 29.62% | 3 | 12.50% |
Eric Dumazet | 15 | 4.78% | 1 | 4.17% |
Daniel Lezcano | 10 | 3.18% | 2 | 8.33% |
Hideaki Yoshifuji / 吉藤英明 | 9 | 2.87% | 4 | 16.67% |
Linus Torvalds | 8 | 2.55% | 1 | 4.17% |
Madhu Challa | 7 | 2.23% | 1 | 4.17% |
David S. Miller | 3 | 0.96% | 1 | 4.17% |
Alexey Dobriyan | 2 | 0.64% | 1 | 4.17% |
Ian Morris | 2 | 0.64% | 1 | 4.17% |
Américo Wang | 2 | 0.64% | 2 | 8.33% |
Eric W. Biedermann | 1 | 0.32% | 1 | 4.17% |
Al Viro | 1 | 0.32% | 1 | 4.17% |
Marcelo Ricardo Leitner | 1 | 0.32% | 1 | 4.17% |
Total | 314 | 100.00% | 24 | 100.00% |
EXPORT_SYMBOL(ipv6_sock_mc_join);
/*
* socket leave on multicast group
*/
int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst;
struct ipv6_mc_socklist __rcu **lnk;
struct net *net = sock_net(sk);
ASSERT_RTNL();
if (!ipv6_addr_is_multicast(addr))
return -EINVAL;
for (lnk = &np->ipv6_mc_list;
(mc_lst = rtnl_dereference(*lnk)) != NULL;
lnk = &mc_lst->next) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) {
struct net_device *dev;
*lnk = mc_lst->next;
dev = __dev_get_by_index(net, mc_lst->ifindex);
if (dev) {
struct inet6_dev *idev = __in6_dev_get(dev);
(void) ip6_mc_leave_src(sk, mc_lst, idev);
if (idev)
__ipv6_dev_mc_dec(idev, &mc_lst->addr);
} else
(void) ip6_mc_leave_src(sk, mc_lst, NULL);
atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
kfree_rcu(mc_lst, rcu);
return 0;
}
}
return -EADDRNOTAVAIL;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 115 | 50.44% | 6 | 25.00% |
David L Stevens | 40 | 17.54% | 2 | 8.33% |
Eric Dumazet | 18 | 7.89% | 2 | 8.33% |
Daniel Lezcano | 12 | 5.26% | 1 | 4.17% |
Li Wei | 12 | 5.26% | 1 | 4.17% |
Madhu Challa | 8 | 3.51% | 1 | 4.17% |
Alexey Kuznetsov | 8 | 3.51% | 1 | 4.17% |
Hideaki Yoshifuji / 吉藤英明 | 6 | 2.63% | 4 | 16.67% |
David S. Miller | 3 | 1.32% | 1 | 4.17% |
Américo Wang | 2 | 0.88% | 2 | 8.33% |
Lai Jiangshan | 2 | 0.88% | 1 | 4.17% |
Marcelo Ricardo Leitner | 1 | 0.44% | 1 | 4.17% |
Eric W. Biedermann | 1 | 0.44% | 1 | 4.17% |
Total | 228 | 100.00% | 24 | 100.00% |
EXPORT_SYMBOL(ipv6_sock_mc_drop);
/* called with rcu_read_lock() */
static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,
const struct in6_addr *group,
int ifindex)
{
struct net_device *dev = NULL;
struct inet6_dev *idev = NULL;
if (ifindex == 0) {
struct rt6_info *rt = rt6_lookup(net, group, NULL, 0, 0);
if (rt) {
dev = rt->dst.dev;
ip6_rt_put(rt);
}
} else
dev = dev_get_by_index_rcu(net, ifindex);
if (!dev)
return NULL;
idev = __in6_dev_get(dev);
if (!idev)
return NULL;
read_lock_bh(&idev->lock);
if (idev->dead) {
read_unlock_bh(&idev->lock);
return NULL;
}
return idev;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David L Stevens | 67 | 46.53% | 1 | 6.67% |
Linus Torvalds (pre-git) | 43 | 29.86% | 5 | 33.33% |
Eric Dumazet | 17 | 11.81% | 2 | 13.33% |
Daniel Lezcano | 8 | 5.56% | 2 | 13.33% |
Al Viro | 3 | 2.08% | 1 | 6.67% |
David S. Miller | 3 | 2.08% | 1 | 6.67% |
Américo Wang | 1 | 0.69% | 1 | 6.67% |
Eric W. Biedermann | 1 | 0.69% | 1 | 6.67% |
Adrian Bunk | 1 | 0.69% | 1 | 6.67% |
Total | 144 | 100.00% | 15 | 100.00% |
void __ipv6_sock_mc_close(struct sock *sk)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst;
struct net *net = sock_net(sk);
ASSERT_RTNL();
while ((mc_lst = rtnl_dereference(np->ipv6_mc_list)) != NULL) {
struct net_device *dev;
np->ipv6_mc_list = mc_lst->next;
dev = __dev_get_by_index(net, mc_lst->ifindex);
if (dev) {
struct inet6_dev *idev = __in6_dev_get(dev);
(void) ip6_mc_leave_src(sk, mc_lst, idev);
if (idev)
__ipv6_dev_mc_dec(idev, &mc_lst->addr);
} else
(void) ip6_mc_leave_src(sk, mc_lst, NULL);
atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc);
kfree_rcu(mc_lst, rcu);
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David L Stevens | 78 | 50.32% | 2 | 12.50% |
Linus Torvalds (pre-git) | 30 | 19.35% | 2 | 12.50% |
Eric Dumazet | 14 | 9.03% | 2 | 12.50% |
David S. Miller | 11 | 7.10% | 1 | 6.25% |
Daniel Lezcano | 8 | 5.16% | 1 | 6.25% |
Américo Wang | 5 | 3.23% | 3 | 18.75% |
Hideaki Yoshifuji / 吉藤英明 | 4 | 2.58% | 2 | 12.50% |
Lai Jiangshan | 2 | 1.29% | 1 | 6.25% |
Sabrina Dubroca | 2 | 1.29% | 1 | 6.25% |
Eric W. Biedermann | 1 | 0.65% | 1 | 6.25% |
Total | 155 | 100.00% | 16 | 100.00% |
void ipv6_sock_mc_close(struct sock *sk)
{
struct ipv6_pinfo *np = inet6_sk(sk);
if (!rcu_access_pointer(np->ipv6_mc_list))
return;
rtnl_lock();
__ipv6_sock_mc_close(sk);
rtnl_unlock();
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Américo Wang | 38 | 90.48% | 1 | 33.33% |
Sabrina Dubroca | 3 | 7.14% | 1 | 33.33% |
Linus Torvalds (pre-git) | 1 | 2.38% | 1 | 33.33% |
Total | 42 | 100.00% | 3 | 100.00% |
int ip6_mc_source(int add, int omode, struct sock *sk,
struct group_source_req *pgsr)
{
struct in6_addr *source, *group;
struct ipv6_mc_socklist *pmc;
struct inet6_dev *idev;
struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct ip6_sf_socklist *psl;
struct net *net = sock_net(sk);
int i, j, rv;
int leavegroup = 0;
int pmclocked = 0;
int err;
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
group = &((struct sockaddr_in6 *)&pgsr->gsr_group)->sin6_addr;
if (!ipv6_addr_is_multicast(group))
return -EINVAL;
rcu_read_lock();
idev = ip6_mc_find_dev_rcu(net, group, pgsr->gsr_interface);
if (!idev) {
rcu_read_unlock();
return -ENODEV;
}
err = -EADDRNOTAVAIL;
for_each_pmc_rcu(inet6, pmc) {
if (pgsr->gsr_interface && pmc->ifindex != pgsr->gsr_interface)
continue;
if (ipv6_addr_equal(&pmc->addr, group))
break;
}
if (!pmc) { /* must have a prior join */
err = -EINVAL;
goto done;
}
/* if a source filter was set, must be the same mode as before */
if (pmc->sflist) {
if (pmc->sfmode != omode) {
err = -EINVAL;
goto done;
}
} else if (pmc->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, pmc->sfmode, 0, NULL, 0);
pmc->sfmode = omode;
}
write_lock(&pmc->sflock);
pmclocked = 1;
psl = pmc->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)
break;
}
if (rv) /* source not found */
goto done; /* err = -EADDRNOTAVAIL */
/* special case - (INCLUDE, empty) == LEAVE_GROUP */
if (psl->sl_count == 1 && omode == MCAST_INCLUDE) {
leavegroup = 1;
goto done;
}
/* update the interface filter */
ip6_mc_del_src(idev, group, omode, 1, source, 1);
for (j = i+1; j < psl->sl_count; j++)
psl->sl_addr[j-1] = psl->sl_addr[j];
psl->sl_count--;
err = 0;
goto done;
}
/* else, add a new source to the filter */
if (psl && psl->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));
}
pmc->sflist = psl = newpsl;
}
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. */
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++;
err = 0;
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
done:
if (pmclocked)
write_unlock(&pmc->sflock);
read_unlock_bh(&idev->lock);
rcu_read_unlock();
if (leavegroup)
err = ipv6_sock_mc_drop(sk, pgsr->gsr_interface, group);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David L Stevens | 581 | 74.30% | 7 | 26.92% |
Linus Torvalds (pre-git) | 147 | 18.80% | 7 | 26.92% |
Eric Dumazet | 17 | 2.17% | 2 | 7.69% |
Chris Wright | 10 | 1.28% | 1 | 3.85% |
Daniel Lezcano | 9 | 1.15% | 1 | 3.85% |
Hideaki Yoshifuji / 吉藤英明 | 9 | 1.15% | 4 | 15.38% |
Marcelo Ricardo Leitner | 3 | 0.38% | 1 | 3.85% |
Al Viro | 2 | 0.26% | 1 | 3.85% |
Andrew Morton | 2 | 0.26% | 1 | 3.85% |
Jean Sacren | 2 | 0.26% | 1 | 3.85% |
Total | 782 | 100.00% | 26 | 100.00% |
int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
{
const struct in6_addr *group;
struct ipv6_mc_socklist *pmc;
struct inet6_dev *idev;
struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct ip6_sf_socklist *newpsl, *psl;
struct net *net = sock_net(sk);
int leavegroup = 0;
int i, err;
group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
if (!ipv6_addr_is_multicast(group))
return -EINVAL;
if (gsf->gf_fmode != MCAST_INCLUDE &&
gsf->gf_fmode != MCAST_EXCLUDE)
return -EINVAL;
rcu_read_lock();
idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface);
if (!idev) {
rcu_read_unlock();
return -ENODEV;
}
err = 0;
if (gsf->gf_fmode == MCAST_INCLUDE && gsf->gf_numsrc == 0) {
leavegroup = 1;
goto done;
}
for_each_pmc_rcu(inet6, pmc) {
if (pmc->ifindex != gsf->gf_interface)
continue;
if (ipv6_addr_equal(&pmc->addr, group))
break;
}
if (!pmc) { /* must have a prior join */
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) {
struct sockaddr_in6 *psin6;
psin6 = (struct sockaddr_in6 *)&gsf->gf_slist[i];
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;
(void) ip6_mc_add_src(idev, group, gsf->gf_fmode, 0, NULL, 0);
}
write_lock(&pmc->sflock);
psl = pmc->sflist;
if (psl) {
(void) ip6_mc_del_src(idev, group, pmc->sfmode,
psl->sl_count, psl->sl_addr, 0);
sock_kfree_s(sk, psl, IP6_SFLSIZE(psl->sl_max));
} else
(void) ip6_mc_del_src(idev, group, pmc->sfmode, 0, NULL, 0);
pmc->sflist = newpsl;
pmc->sfmode = gsf->gf_fmode;
write_unlock(&pmc->sflock);
err = 0;
done:
read_unlock_bh(&idev->lock);
rcu_read_unlock();
if (leavegroup)
err = ipv6_sock_mc_drop(sk, gsf->gf_interface, group);
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David L Stevens | 393 | 74.01% | 4 | 21.05% |
Linus Torvalds (pre-git) | 76 | 14.31% | 5 | 26.32% |
Yan Zheng | 22 | 4.14% | 1 | 5.26% |
Eric Dumazet | 20 | 3.77% | 3 | 15.79% |
Daniel Lezcano | 9 | 1.69% | 1 | 5.26% |
Hideaki Yoshifuji / 吉藤英明 | 5 | 0.94% | 3 | 15.79% |
David S. Miller | 4 | 0.75% | 1 | 5.26% |
Al Viro | 2 | 0.38% | 1 | 5.26% |
Total | 531 | 100.00% | 19 | 100.00% |
int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
struct group_filter __user *optval, int __user *optlen)
{
int err, i, count, copycount;
const struct in6_addr *group;
struct ipv6_mc_socklist *pmc;
struct inet6_dev *idev;
struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct ip6_sf_socklist *psl;
struct net *net = sock_net(sk);
group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
if (!ipv6_addr_is_multicast(group))
return -EINVAL;
rcu_read_lock();
idev = ip6_mc_find_dev_rcu(net, group, gsf->gf_interface);
if (!idev) {
rcu_read_unlock();
return -ENODEV;
}
err = -EADDRNOTAVAIL;
/* changes to the ipv6_mc_list require the socket lock and
* rtnl lock. We have the socket lock and rcu read lock,
* so reading the list is safe.
*/
for_each_pmc_rcu(inet6, pmc) {
if (pmc->ifindex != gsf->gf_interface)
continue;
if (ipv6_addr_equal(group, &pmc->addr))
break;
}
if (!pmc) /* must have a prior join */
goto done;
gsf->gf_fmode = pmc->sfmode;
psl = pmc->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;
if (put_user(GROUP_FILTER_SIZE(copycount), optlen) ||
copy_to_user(optval, gsf, GROUP_FILTER_SIZE(0))) {
return -EFAULT;
}
/* changes to psl require the socket lock, and a write lock
* on pmc->sflock. We have the socket lock so reading here is safe.
*/
for (i = 0; i < copycount; i++) {
struct sockaddr_in6 *psin6;
struct sockaddr_storage ss;
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(&optval->gf_slist[i], &ss, sizeof(ss)))
return -EFAULT;
}
return 0;
done:
read_unlock_bh(&idev->lock);
rcu_read_unlock();
return err;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
David L Stevens | 259 | 68.88% | 1 | 6.25% |
Linus Torvalds (pre-git) | 79 | 21.01% | 6 | 37.50% |
Eric Dumazet | 20 | 5.32% | 3 | 18.75% |
Daniel Lezcano | 9 | 2.39% | 1 | 6.25% |
Hideaki Yoshifuji / 吉藤英明 | 5 | 1.33% | 3 | 18.75% |
Al Viro | 2 | 0.53% | 1 | 6.25% |
Américo Wang | 2 | 0.53% | 1 | 6.25% |
Total | 376 | 100.00% | 16 | 100.00% |
bool inet6_mc_check(struct sock *sk, const struct in6_addr *mc_addr,
const struct in6_addr *src_addr)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc;
struct ip6_sf_socklist *psl;
bool rv = true;
rcu_read_lock()