[NETNS][IPV6] mcast - handle several network namespace

This patch make use of the network namespace information at the right
places to handle the multicast for several network namespaces.  It
makes the socket control to be per namespace too.

Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
Signed-off-by: Benjamin Thery <benjamin.thery@bull.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Daniel Lezcano 2008-03-07 11:16:55 -08:00 committed by David S. Miller
parent e504799276
commit b8ad0cbc58
3 changed files with 83 additions and 39 deletions

View file

@ -53,5 +53,6 @@ struct netns_ipv6 {
struct sock **icmp_sk; struct sock **icmp_sk;
struct sock *ndisc_sk; struct sock *ndisc_sk;
struct sock *tcp_sk; struct sock *tcp_sk;
struct sock *igmp_sk;
}; };
#endif #endif

View file

@ -126,8 +126,6 @@ static struct in6_addr mld2_all_mcr = MLD2_ALL_MCR_INIT;
/* Big mc list lock for all the sockets */ /* Big mc list lock for all the sockets */
static DEFINE_RWLOCK(ipv6_sk_mc_lock); static DEFINE_RWLOCK(ipv6_sk_mc_lock);
static struct socket *igmp6_socket;
int __ipv6_dev_mc_dec(struct inet6_dev *idev, struct in6_addr *addr); int __ipv6_dev_mc_dec(struct inet6_dev *idev, struct in6_addr *addr);
static void igmp6_join_group(struct ifmcaddr6 *ma); static void igmp6_join_group(struct ifmcaddr6 *ma);
@ -183,6 +181,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
struct net_device *dev = NULL; struct net_device *dev = NULL;
struct ipv6_mc_socklist *mc_lst; struct ipv6_mc_socklist *mc_lst;
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct net *net = sk->sk_net;
int err; int err;
if (!ipv6_addr_is_multicast(addr)) if (!ipv6_addr_is_multicast(addr))
@ -208,14 +207,14 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
if (ifindex == 0) { if (ifindex == 0) {
struct rt6_info *rt; struct rt6_info *rt;
rt = rt6_lookup(&init_net, addr, NULL, 0, 0); rt = rt6_lookup(net, addr, NULL, 0, 0);
if (rt) { if (rt) {
dev = rt->rt6i_dev; dev = rt->rt6i_dev;
dev_hold(dev); dev_hold(dev);
dst_release(&rt->u.dst); dst_release(&rt->u.dst);
} }
} else } else
dev = dev_get_by_index(&init_net, ifindex); dev = dev_get_by_index(net, ifindex);
if (dev == NULL) { if (dev == NULL) {
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
@ -256,6 +255,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
{ {
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst, **lnk; struct ipv6_mc_socklist *mc_lst, **lnk;
struct net *net = sk->sk_net;
write_lock_bh(&ipv6_sk_mc_lock); write_lock_bh(&ipv6_sk_mc_lock);
for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) { for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
@ -266,7 +266,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
*lnk = mc_lst->next; *lnk = mc_lst->next;
write_unlock_bh(&ipv6_sk_mc_lock); write_unlock_bh(&ipv6_sk_mc_lock);
if ((dev = dev_get_by_index(&init_net, mc_lst->ifindex)) != NULL) { dev = dev_get_by_index(net, mc_lst->ifindex);
if (dev != NULL) {
struct inet6_dev *idev = in6_dev_get(dev); struct inet6_dev *idev = in6_dev_get(dev);
(void) ip6_mc_leave_src(sk, mc_lst, idev); (void) ip6_mc_leave_src(sk, mc_lst, idev);
@ -286,7 +287,9 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
} }
static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex) static struct inet6_dev *ip6_mc_find_dev(struct net *net,
struct in6_addr *group,
int ifindex)
{ {
struct net_device *dev = NULL; struct net_device *dev = NULL;
struct inet6_dev *idev = NULL; struct inet6_dev *idev = NULL;
@ -294,14 +297,14 @@ static struct inet6_dev *ip6_mc_find_dev(struct in6_addr *group, int ifindex)
if (ifindex == 0) { if (ifindex == 0) {
struct rt6_info *rt; struct rt6_info *rt;
rt = rt6_lookup(&init_net, group, NULL, 0, 0); rt = rt6_lookup(net, group, NULL, 0, 0);
if (rt) { if (rt) {
dev = rt->rt6i_dev; dev = rt->rt6i_dev;
dev_hold(dev); dev_hold(dev);
dst_release(&rt->u.dst); dst_release(&rt->u.dst);
} }
} else } else
dev = dev_get_by_index(&init_net, ifindex); dev = dev_get_by_index(net, ifindex);
if (!dev) if (!dev)
return NULL; return NULL;
@ -324,6 +327,7 @@ void ipv6_sock_mc_close(struct sock *sk)
{ {
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6_mc_socklist *mc_lst; struct ipv6_mc_socklist *mc_lst;
struct net *net = sk->sk_net;
write_lock_bh(&ipv6_sk_mc_lock); write_lock_bh(&ipv6_sk_mc_lock);
while ((mc_lst = np->ipv6_mc_list) != NULL) { while ((mc_lst = np->ipv6_mc_list) != NULL) {
@ -332,7 +336,7 @@ void ipv6_sock_mc_close(struct sock *sk)
np->ipv6_mc_list = mc_lst->next; np->ipv6_mc_list = mc_lst->next;
write_unlock_bh(&ipv6_sk_mc_lock); write_unlock_bh(&ipv6_sk_mc_lock);
dev = dev_get_by_index(&init_net, mc_lst->ifindex); dev = dev_get_by_index(net, mc_lst->ifindex);
if (dev) { if (dev) {
struct inet6_dev *idev = in6_dev_get(dev); struct inet6_dev *idev = in6_dev_get(dev);
@ -361,6 +365,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
struct inet6_dev *idev; struct inet6_dev *idev;
struct ipv6_pinfo *inet6 = inet6_sk(sk); struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct ip6_sf_socklist *psl; struct ip6_sf_socklist *psl;
struct net *net = sk->sk_net;
int i, j, rv; int i, j, rv;
int leavegroup = 0; int leavegroup = 0;
int pmclocked = 0; int pmclocked = 0;
@ -376,7 +381,7 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
if (!ipv6_addr_is_multicast(group)) if (!ipv6_addr_is_multicast(group))
return -EINVAL; return -EINVAL;
idev = ip6_mc_find_dev(group, pgsr->gsr_interface); idev = ip6_mc_find_dev(net, group, pgsr->gsr_interface);
if (!idev) if (!idev)
return -ENODEV; return -ENODEV;
dev = idev->dev; dev = idev->dev;
@ -500,6 +505,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
struct inet6_dev *idev; struct inet6_dev *idev;
struct ipv6_pinfo *inet6 = inet6_sk(sk); struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct ip6_sf_socklist *newpsl, *psl; struct ip6_sf_socklist *newpsl, *psl;
struct net *net = sk->sk_net;
int leavegroup = 0; int leavegroup = 0;
int i, err; int i, err;
@ -511,7 +517,7 @@ int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf)
gsf->gf_fmode != MCAST_EXCLUDE) gsf->gf_fmode != MCAST_EXCLUDE)
return -EINVAL; return -EINVAL;
idev = ip6_mc_find_dev(group, gsf->gf_interface); idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
if (!idev) if (!idev)
return -ENODEV; return -ENODEV;
@ -592,13 +598,14 @@ int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf,
struct net_device *dev; struct net_device *dev;
struct ipv6_pinfo *inet6 = inet6_sk(sk); struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct ip6_sf_socklist *psl; struct ip6_sf_socklist *psl;
struct net *net = sk->sk_net;
group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr; group = &((struct sockaddr_in6 *)&gsf->gf_group)->sin6_addr;
if (!ipv6_addr_is_multicast(group)) if (!ipv6_addr_is_multicast(group))
return -EINVAL; return -EINVAL;
idev = ip6_mc_find_dev(group, gsf->gf_interface); idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
if (!idev) if (!idev)
return -ENODEV; return -ENODEV;
@ -1393,7 +1400,8 @@ mld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted)
static struct sk_buff *mld_newpack(struct net_device *dev, int size) static struct sk_buff *mld_newpack(struct net_device *dev, int size)
{ {
struct sock *sk = igmp6_socket->sk; struct net *net = dev->nd_net;
struct sock *sk = net->ipv6.igmp_sk;
struct sk_buff *skb; struct sk_buff *skb;
struct mld2_report *pmr; struct mld2_report *pmr;
struct in6_addr addr_buf; struct in6_addr addr_buf;
@ -1440,6 +1448,7 @@ static void mld_sendpack(struct sk_buff *skb)
(struct mld2_report *)skb_transport_header(skb); (struct mld2_report *)skb_transport_header(skb);
int payload_len, mldlen; int payload_len, mldlen;
struct inet6_dev *idev = in6_dev_get(skb->dev); struct inet6_dev *idev = in6_dev_get(skb->dev);
struct net *net = skb->dev->nd_net;
int err; int err;
struct flowi fl; struct flowi fl;
@ -1459,7 +1468,7 @@ static void mld_sendpack(struct sk_buff *skb)
goto err_out; goto err_out;
} }
icmpv6_flow_init(igmp6_socket->sk, &fl, ICMPV6_MLD2_REPORT, icmpv6_flow_init(net->ipv6.igmp_sk, &fl, ICMPV6_MLD2_REPORT,
&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
skb->dev->ifindex); skb->dev->ifindex);
@ -1753,7 +1762,8 @@ static void mld_send_cr(struct inet6_dev *idev)
static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
{ {
struct sock *sk = igmp6_socket->sk; struct net *net = dev->nd_net;
struct sock *sk = net->ipv6.igmp_sk;
struct inet6_dev *idev; struct inet6_dev *idev;
struct sk_buff *skb; struct sk_buff *skb;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
@ -1824,7 +1834,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
goto err_out; goto err_out;
} }
icmpv6_flow_init(igmp6_socket->sk, &fl, type, icmpv6_flow_init(sk, &fl, type,
&ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
skb->dev->ifindex); skb->dev->ifindex);
@ -2334,6 +2344,7 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
struct igmp6_mc_iter_state { struct igmp6_mc_iter_state {
struct seq_net_private p;
struct net_device *dev; struct net_device *dev;
struct inet6_dev *idev; struct inet6_dev *idev;
}; };
@ -2344,9 +2355,10 @@ static inline struct ifmcaddr6 *igmp6_mc_get_first(struct seq_file *seq)
{ {
struct ifmcaddr6 *im = NULL; struct ifmcaddr6 *im = NULL;
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq); struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
struct net *net = state->p.net;
state->idev = NULL; state->idev = NULL;
for_each_netdev(&init_net, state->dev) { for_each_netdev(net, state->dev) {
struct inet6_dev *idev; struct inet6_dev *idev;
idev = in6_dev_get(state->dev); idev = in6_dev_get(state->dev);
if (!idev) if (!idev)
@ -2448,8 +2460,8 @@ static const struct seq_operations igmp6_mc_seq_ops = {
static int igmp6_mc_seq_open(struct inode *inode, struct file *file) static int igmp6_mc_seq_open(struct inode *inode, struct file *file)
{ {
return seq_open_private(file, &igmp6_mc_seq_ops, return seq_open_net(inode, file, &igmp6_mc_seq_ops,
sizeof(struct igmp6_mc_iter_state)); sizeof(struct igmp6_mc_iter_state));
} }
static const struct file_operations igmp6_mc_seq_fops = { static const struct file_operations igmp6_mc_seq_fops = {
@ -2457,10 +2469,11 @@ static const struct file_operations igmp6_mc_seq_fops = {
.open = igmp6_mc_seq_open, .open = igmp6_mc_seq_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = seq_release_private, .release = seq_release_net,
}; };
struct igmp6_mcf_iter_state { struct igmp6_mcf_iter_state {
struct seq_net_private p;
struct net_device *dev; struct net_device *dev;
struct inet6_dev *idev; struct inet6_dev *idev;
struct ifmcaddr6 *im; struct ifmcaddr6 *im;
@ -2473,10 +2486,11 @@ static inline struct ip6_sf_list *igmp6_mcf_get_first(struct seq_file *seq)
struct ip6_sf_list *psf = NULL; struct ip6_sf_list *psf = NULL;
struct ifmcaddr6 *im = NULL; struct ifmcaddr6 *im = NULL;
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq); struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
struct net *net = state->p.net;
state->idev = NULL; state->idev = NULL;
state->im = NULL; state->im = NULL;
for_each_netdev(&init_net, state->dev) { for_each_netdev(net, state->dev) {
struct inet6_dev *idev; struct inet6_dev *idev;
idev = in6_dev_get(state->dev); idev = in6_dev_get(state->dev);
if (unlikely(idev == NULL)) if (unlikely(idev == NULL))
@ -2608,8 +2622,8 @@ static const struct seq_operations igmp6_mcf_seq_ops = {
static int igmp6_mcf_seq_open(struct inode *inode, struct file *file) static int igmp6_mcf_seq_open(struct inode *inode, struct file *file)
{ {
return seq_open_private(file, &igmp6_mcf_seq_ops, return seq_open_net(inode, file, &igmp6_mcf_seq_ops,
sizeof(struct igmp6_mcf_iter_state)); sizeof(struct igmp6_mcf_iter_state));
} }
static const struct file_operations igmp6_mcf_seq_fops = { static const struct file_operations igmp6_mcf_seq_fops = {
@ -2617,26 +2631,27 @@ static const struct file_operations igmp6_mcf_seq_fops = {
.open = igmp6_mcf_seq_open, .open = igmp6_mcf_seq_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = seq_release_private, .release = seq_release_net,
}; };
#endif #endif
int __init igmp6_init(void) static int igmp6_net_init(struct net *net)
{ {
struct ipv6_pinfo *np; struct ipv6_pinfo *np;
struct socket *sock;
struct sock *sk; struct sock *sk;
int err; int err;
err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &igmp6_socket); err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &sock);
if (err < 0) { if (err < 0) {
printk(KERN_ERR printk(KERN_ERR
"Failed to initialize the IGMP6 control socket (err %d).\n", "Failed to initialize the IGMP6 control socket (err %d).\n",
err); err);
igmp6_socket = NULL; /* For safety. */ goto out;
return err;
} }
sk = igmp6_socket->sk; net->ipv6.igmp_sk = sk = sock->sk;
sk_change_net(sk, net);
sk->sk_allocation = GFP_ATOMIC; sk->sk_allocation = GFP_ATOMIC;
sk->sk_prot->unhash(sk); sk->sk_prot->unhash(sk);
@ -2644,20 +2659,45 @@ int __init igmp6_init(void)
np->hop_limit = 1; np->hop_limit = 1;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
proc_net_fops_create(&init_net, "igmp6", S_IRUGO, &igmp6_mc_seq_fops); err = -ENOMEM;
proc_net_fops_create(&init_net, "mcfilter6", S_IRUGO, &igmp6_mcf_seq_fops); if (!proc_net_fops_create(net, "igmp6", S_IRUGO, &igmp6_mc_seq_fops))
goto out_sock_create;
if (!proc_net_fops_create(net, "mcfilter6", S_IRUGO,
&igmp6_mcf_seq_fops)) {
proc_net_remove(net, "igmp6");
goto out_sock_create;
}
#endif #endif
return 0; err = 0;
out:
return err;
out_sock_create:
sk_release_kernel(net->ipv6.igmp_sk);
goto out;
}
static void igmp6_net_exit(struct net *net)
{
sk_release_kernel(net->ipv6.igmp_sk);
#ifdef CONFIG_PROC_FS
proc_net_remove(net, "mcfilter6");
proc_net_remove(net, "igmp6");
#endif
}
static struct pernet_operations igmp6_net_ops = {
.init = igmp6_net_init,
.exit = igmp6_net_exit,
};
int __init igmp6_init(void)
{
return register_pernet_subsys(&igmp6_net_ops);
} }
void igmp6_cleanup(void) void igmp6_cleanup(void)
{ {
sock_release(igmp6_socket); unregister_pernet_subsys(&igmp6_net_ops);
igmp6_socket = NULL; /* for safety */
#ifdef CONFIG_PROC_FS
proc_net_remove(&init_net, "mcfilter6");
proc_net_remove(&init_net, "igmp6");
#endif
} }

View file

@ -323,6 +323,9 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
sk_for_each_from(s, node) { sk_for_each_from(s, node) {
struct inet_sock *inet = inet_sk(s); struct inet_sock *inet = inet_sk(s);
if (s->sk_net != sk->sk_net)
continue;
if (s->sk_hash == num && s->sk_family == PF_INET6) { if (s->sk_hash == num && s->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(s); struct ipv6_pinfo *np = inet6_sk(s);
if (inet->dport) { if (inet->dport) {