/*
* Multicast support for IPv6
- * Linux INET6 implementation
+ * Linux INET6 implementation
*
* Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
+ * Pedro Roque <roque@di.fc.ul.pt>
*
* $Id: mcast.c,v 1.40 2002/02/08 03:57:19 davem Exp $
*
- * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
+ * 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
* - MLDv2 support
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
+#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/snmp.h>
struct mld2_grec {
__u8 grec_type;
__u8 grec_auxwords;
- __u16 grec_nsrcs;
+ __be16 grec_nsrcs;
struct in6_addr grec_mca;
struct in6_addr grec_src[0];
};
struct mld2_report {
__u8 type;
__u8 resv1;
- __u16 csum;
- __u16 resv2;
- __u16 ngrec;
+ __sum16 csum;
+ __be16 resv2;
+ __be16 ngrec;
struct mld2_grec grec[0];
};
struct mld2_query {
__u8 type;
__u8 code;
- __u16 csum;
- __u16 mrc;
- __u16 resv1;
+ __sum16 csum;
+ __be16 mrc;
+ __be16 resv1;
struct in6_addr mca;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 qrv:3,
#error "Please fix <asm/byteorder.h>"
#endif
__u8 qqic;
- __u16 nsrcs;
+ __be16 nsrcs;
struct in6_addr srcs[0];
};
#define IPV6_MLD_MAX_MSF 64
-int sysctl_mld_max_msf = IPV6_MLD_MAX_MSF;
+int sysctl_mld_max_msf __read_mostly = IPV6_MLD_MAX_MSF;
/*
* socket join on multicast group
dst_release(&rt->u.dst);
}
} else
- dev = dev_get_by_index(ifindex);
+ dev = dev_get_by_index(&init_net, ifindex);
if (dev == NULL) {
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
*lnk = mc_lst->next;
write_unlock_bh(&ipv6_sk_mc_lock);
- if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) {
+ if ((dev = dev_get_by_index(&init_net, mc_lst->ifindex)) != NULL) {
struct inet6_dev *idev = in6_dev_get(dev);
+ (void) ip6_mc_leave_src(sk, mc_lst, idev);
if (idev) {
- (void) ip6_mc_leave_src(sk,mc_lst,idev);
__ipv6_dev_mc_dec(idev, &mc_lst->addr);
in6_dev_put(idev);
}
dev_put(dev);
- }
+ } else
+ (void) ip6_mc_leave_src(sk, mc_lst, NULL);
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
return 0;
}
dst_release(&rt->u.dst);
}
} else
- dev = dev_get_by_index(ifindex);
+ dev = dev_get_by_index(&init_net, ifindex);
if (!dev)
return NULL;
np->ipv6_mc_list = mc_lst->next;
write_unlock_bh(&ipv6_sk_mc_lock);
- dev = dev_get_by_index(mc_lst->ifindex);
+ dev = dev_get_by_index(&init_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) {
- (void) ip6_mc_leave_src(sk, mc_lst, idev);
__ipv6_dev_mc_dec(idev, &mc_lst->addr);
in6_dev_put(idev);
}
dev_put(dev);
- }
+ } else
+ (void) ip6_mc_leave_src(sk, mc_lst, NULL);
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
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)))
+ if (copy_to_user(&optval->gf_slist[i], &ss, sizeof(ss)))
return -EFAULT;
}
return 0;
* for deleted items allows change reports to use common code with
* non-deleted or query-response MCA's.
*/
- pmc = kmalloc(sizeof(*pmc), GFP_ATOMIC);
+ pmc = kzalloc(sizeof(*pmc), GFP_ATOMIC);
if (!pmc)
return;
- memset(pmc, 0, sizeof(*pmc));
+
spin_lock_bh(&im->mca_lock);
spin_lock_init(&pmc->mca_lock);
pmc->idev = im->idev;
* not found: create a new one.
*/
- mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
+ mc = kzalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
if (mc == NULL) {
write_unlock_bh(&idev->lock);
return -ENOMEM;
}
- memset(mc, 0, sizeof(struct ifmcaddr6));
init_timer(&mc->mca_timer);
mc->mca_timer.function = igmp6_timer_handler;
mc->mca_timer.data = (unsigned long) mc;
if (!pskb_may_pull(skb, sizeof(struct icmp6hdr)))
return 0;
- pic = (struct icmp6hdr *)skb->h.raw;
+ pic = icmp6_hdr(skb);
switch (pic->icmp6_type) {
case ICMPV6_MGM_QUERY:
return -EINVAL;
/* compute payload length excluding extension headers */
- len = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
- len -= (char *)skb->h.raw - (char *)skb->nh.ipv6h;
+ len = ntohs(ipv6_hdr(skb)->payload_len) + sizeof(struct ipv6hdr);
+ len -= skb_network_header_len(skb);
/* Drop queries with not link local source */
- if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
+ if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL))
return -EINVAL;
idev = in6_dev_get(skb->dev);
if (idev == NULL)
return 0;
- hdr = (struct icmp6hdr *) skb->h.raw;
+ hdr = icmp6_hdr(skb);
group = (struct in6_addr *) (hdr + 1);
group_type = ipv6_addr_type(group);
/* clear deleted report items */
mld_clear_delrec(idev);
} else if (len >= 28) {
- int srcs_offset = sizeof(struct mld2_query) -
+ int srcs_offset = sizeof(struct mld2_query) -
sizeof(struct icmp6hdr);
if (!pskb_may_pull(skb, srcs_offset)) {
in6_dev_put(idev);
return -EINVAL;
}
- mlh2 = (struct mld2_query *) skb->h.raw;
+ mlh2 = (struct mld2_query *)skb_transport_header(skb);
max_delay = (MLDV2_MRC(ntohs(mlh2->mrc))*HZ)/1000;
if (!max_delay)
max_delay = 1;
}
/* mark sources to include, if group & source-specific */
if (mlh2->nsrcs != 0) {
- if (!pskb_may_pull(skb, srcs_offset +
+ if (!pskb_may_pull(skb, srcs_offset +
ntohs(mlh2->nsrcs) * sizeof(struct in6_addr))) {
in6_dev_put(idev);
return -EINVAL;
}
- mlh2 = (struct mld2_query *) skb->h.raw;
+ mlh2 = (struct mld2_query *)skb_transport_header(skb);
mark = 1;
}
} else {
if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
return -EINVAL;
- hdr = (struct icmp6hdr*) skb->h.raw;
+ hdr = icmp6_hdr(skb);
/* Drop reports with not link local source */
- addr_type = ipv6_addr_type(&skb->nh.ipv6h->saddr);
- if (addr_type != IPV6_ADDR_ANY &&
+ addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);
+ if (addr_type != IPV6_ADDR_ANY &&
!(addr_type&IPV6_ADDR_LINKLOCAL))
return -EINVAL;
/* we assume size > sizeof(ra) here */
skb = sock_alloc_send_skb(sk, size + LL_RESERVED_SPACE(dev), 1, &err);
- if (skb == 0)
+ if (!skb)
return NULL;
skb_reserve(skb, LL_RESERVED_SPACE(dev));
- if (ipv6_get_lladdr(dev, &addr_buf)) {
+ if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
/* <draft-ietf-magma-mld-source-05.txt>:
- * use unspecified address as the source address
+ * use unspecified address as the source address
* when a valid link-local address is not available.
*/
memset(&addr_buf, 0, sizeof(addr_buf));
memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
- pmr =(struct mld2_report *)skb_put(skb, sizeof(*pmr));
- skb->h.raw = (unsigned char *)pmr;
+ skb_set_transport_header(skb, skb_tail_pointer(skb) - skb->data);
+ skb_put(skb, sizeof(*pmr));
+ pmr = (struct mld2_report *)skb_transport_header(skb);
pmr->type = ICMPV6_MLD2_REPORT;
pmr->resv1 = 0;
pmr->csum = 0;
static inline int mld_dev_queue_xmit2(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
+ unsigned char ha[MAX_ADDR_LEN];
- if (dev->hard_header) {
- unsigned char ha[MAX_ADDR_LEN];
- int err;
-
- ndisc_mc_map(&skb->nh.ipv6h->daddr, ha, dev, 1);
- err = dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, skb->len);
- if (err < 0) {
- kfree_skb(skb);
- return err;
- }
+ ndisc_mc_map(&ipv6_hdr(skb)->daddr, ha, dev, 1);
+ if (dev_hard_header(skb, dev, ETH_P_IPV6, ha, NULL, skb->len) < 0) {
+ kfree_skb(skb);
+ return -EINVAL;
}
return dev_queue_xmit(skb);
}
static inline int mld_dev_queue_xmit(struct sk_buff *skb)
{
return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dev,
- mld_dev_queue_xmit2);
+ mld_dev_queue_xmit2);
}
static void mld_sendpack(struct sk_buff *skb)
{
- struct ipv6hdr *pip6 = skb->nh.ipv6h;
- struct mld2_report *pmr = (struct mld2_report *)skb->h.raw;
+ struct ipv6hdr *pip6 = ipv6_hdr(skb);
+ struct mld2_report *pmr =
+ (struct mld2_report *)skb_transport_header(skb);
int payload_len, mldlen;
struct inet6_dev *idev = in6_dev_get(skb->dev);
int err;
- IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
- payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
- sizeof(struct ipv6hdr);
- mldlen = skb->tail - skb->h.raw;
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
+ payload_len = (skb->tail - skb->network_header) - sizeof(*pip6);
+ mldlen = skb->tail - skb->transport_header;
pip6->payload_len = htons(payload_len);
pmr->csum = csum_ipv6_magic(&pip6->saddr, &pip6->daddr, mldlen,
- IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0));
+ IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb),
+ mldlen, 0));
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
mld_dev_queue_xmit);
if (!err) {
- ICMP6_INC_STATS(idev,ICMP6_MIB_OUTMSGS);
- IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
+ ICMP6MSGOUT_INC_STATS_BH(idev, ICMPV6_MLD2_REPORT);
+ ICMP6_INC_STATS_BH(idev, ICMP6_MIB_OUTMSGS);
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTMCASTPKTS);
} else
- IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTDISCARDS);
if (likely(idev != NULL))
in6_dev_put(idev);
pgr->grec_auxwords = 0;
pgr->grec_nsrcs = 0;
pgr->grec_mca = pmc->mca_addr; /* structure copy */
- pmr = (struct mld2_report *)skb->h.raw;
+ pmr = (struct mld2_report *)skb_transport_header(skb);
pmr->ngrec = htons(ntohs(pmr->ngrec)+1);
*ppgr = pgr;
return skb;
if (!*psf_list)
goto empty_source;
- pmr = skb ? (struct mld2_report *)skb->h.raw : NULL;
+ pmr = skb ? (struct mld2_report *)skb_transport_header(skb) : NULL;
/* EX and TO_EX get a fresh packet, if needed */
if (truncate) {
skb = add_grhead(skb, pmc, type, &pgr);
first = 0;
}
+ if (!skb)
+ return NULL;
psrc = (struct in6_addr *)skb_put(skb, sizeof(*psrc));
*psrc = psf->sf_addr;
scount++; stotal++;
{
struct sock *sk = igmp6_socket->sk;
struct inet6_dev *idev;
- struct sk_buff *skb;
- struct icmp6hdr *hdr;
+ struct sk_buff *skb;
+ struct icmp6hdr *hdr;
struct in6_addr *snd_addr;
struct in6_addr *addrp;
struct in6_addr addr_buf;
IPV6_TLV_ROUTERALERT, 2, 0, 0,
IPV6_TLV_PADN, 0 };
- IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
+ rcu_read_lock();
+ IP6_INC_STATS(__in6_dev_get(dev),
+ IPSTATS_MIB_OUTREQUESTS);
+ rcu_read_unlock();
snd_addr = addr;
if (type == ICMPV6_MGM_REDUCTION) {
snd_addr = &all_routers;
skb = sock_alloc_send_skb(sk, LL_RESERVED_SPACE(dev) + full_len, 1, &err);
if (skb == NULL) {
- IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
+ rcu_read_lock();
+ IP6_INC_STATS(__in6_dev_get(dev),
+ IPSTATS_MIB_OUTDISCARDS);
+ rcu_read_unlock();
return;
}
skb_reserve(skb, LL_RESERVED_SPACE(dev));
- if (ipv6_get_lladdr(dev, &addr_buf)) {
+ if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
/* <draft-ietf-magma-mld-source-05.txt>:
- * use unspecified address as the source address
+ * use unspecified address as the source address
* when a valid link-local address is not available.
*/
memset(&addr_buf, 0, sizeof(addr_buf));
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
mld_dev_queue_xmit);
if (!err) {
- if (type == ICMPV6_MGM_REDUCTION)
- ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBREDUCTIONS);
- else
- ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBRESPONSES);
+ ICMP6MSGOUT_INC_STATS(idev, type);
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
- IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
} else
- IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
if (likely(idev != NULL))
in6_dev_put(idev);
psf_prev = psf;
}
if (!psf) {
- psf = kmalloc(sizeof(*psf), GFP_ATOMIC);
+ psf = kzalloc(sizeof(*psf), GFP_ATOMIC);
if (!psf)
return -ENOBUFS;
- memset(psf, 0, sizeof(*psf));
+
psf->sf_addr = *psfsrc;
if (psf_prev) {
psf_prev->sf_next = psf;
new_in = psf->sf_count[MCAST_INCLUDE] != 0;
if (new_in) {
if (!psf->sf_oldin) {
- struct ip6_sf_list *prev = 0;
+ struct ip6_sf_list *prev = NULL;
for (dpsf=pmc->mca_tomb; dpsf;
dpsf=dpsf->sf_next) {
/* callers have the socket lock and a write lock on ipv6_sk_mc_lock,
* so no other readers or writers of iml or its sflist
*/
- if (iml->sflist == 0) {
+ if (!iml->sflist) {
/* any-source empty exclude case */
return ip6_mc_del_src(idev, &iml->addr, iml->sfmode, 0, NULL, 0);
}
void ipv6_mc_init_dev(struct inet6_dev *idev)
{
- struct in6_addr maddr;
-
write_lock_bh(&idev->lock);
rwlock_init(&idev->mc_lock);
idev->mc_gq_running = 0;
idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL;
idev->mc_v1_seen = 0;
write_unlock_bh(&idev->lock);
-
- /* Add all-nodes address. */
- ipv6_addr_all_nodes(&maddr);
- ipv6_dev_mc_inc(idev->dev, &maddr);
}
/*
struct ifmcaddr6 *im = NULL;
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
- for (state->dev = dev_base, state->idev = NULL;
- state->dev;
- state->dev = state->dev->next) {
+ state->idev = NULL;
+ for_each_netdev(&init_net, state->dev) {
struct inet6_dev *idev;
idev = in6_dev_get(state->dev);
if (!idev)
read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev);
}
- state->dev = state->dev->next;
+ state->dev = next_net_device(state->dev);
if (!state->dev) {
state->idev = NULL;
break;
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
seq_printf(seq,
- "%-4d %-15s " NIP6_SEQFMT " %5d %08X %ld\n",
+ "%-4d %-15s " NIP6_SEQFMT " %5d %08X %ld\n",
state->dev->ifindex, state->dev->name,
NIP6(im->mca_addr),
im->mca_users, im->mca_flags,
return 0;
}
-static struct seq_operations igmp6_mc_seq_ops = {
+static const struct seq_operations igmp6_mc_seq_ops = {
.start = igmp6_mc_seq_start,
.next = igmp6_mc_seq_next,
.stop = igmp6_mc_seq_stop,
static int igmp6_mc_seq_open(struct inode *inode, struct file *file)
{
- struct seq_file *seq;
- int rc = -ENOMEM;
- struct igmp6_mc_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
-
- if (!s)
- goto out;
-
- rc = seq_open(file, &igmp6_mc_seq_ops);
- if (rc)
- goto out_kfree;
-
- seq = file->private_data;
- seq->private = s;
- memset(s, 0, sizeof(*s));
-out:
- return rc;
-out_kfree:
- kfree(s);
- goto out;
+ return seq_open_private(file, &igmp6_mc_seq_ops,
+ sizeof(struct igmp6_mc_iter_state));
}
-static struct file_operations igmp6_mc_seq_fops = {
+static const struct file_operations igmp6_mc_seq_fops = {
.owner = THIS_MODULE,
.open = igmp6_mc_seq_open,
.read = seq_read,
struct ifmcaddr6 *im = NULL;
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
- for (state->dev = dev_base, state->idev = NULL, state->im = NULL;
- state->dev;
- state->dev = state->dev->next) {
+ state->idev = NULL;
+ state->im = NULL;
+ for_each_netdev(&init_net, state->dev) {
struct inet6_dev *idev;
idev = in6_dev_get(state->dev);
if (unlikely(idev == NULL))
read_unlock_bh(&state->idev->lock);
in6_dev_put(state->idev);
}
- state->dev = state->dev->next;
+ state->dev = next_net_device(state->dev);
if (!state->dev) {
state->idev = NULL;
goto out;
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
if (v == SEQ_START_TOKEN) {
- seq_printf(seq,
+ seq_printf(seq,
"%3s %6s "
"%32s %32s %6s %6s\n", "Idx",
"Device", "Multicast Address",
return 0;
}
-static struct seq_operations igmp6_mcf_seq_ops = {
+static const struct seq_operations igmp6_mcf_seq_ops = {
.start = igmp6_mcf_seq_start,
.next = igmp6_mcf_seq_next,
.stop = igmp6_mcf_seq_stop,
static int igmp6_mcf_seq_open(struct inode *inode, struct file *file)
{
- struct seq_file *seq;
- int rc = -ENOMEM;
- struct igmp6_mcf_iter_state *s = kmalloc(sizeof(*s), GFP_KERNEL);
-
- if (!s)
- goto out;
-
- rc = seq_open(file, &igmp6_mcf_seq_ops);
- if (rc)
- goto out_kfree;
-
- seq = file->private_data;
- seq->private = s;
- memset(s, 0, sizeof(*s));
-out:
- return rc;
-out_kfree:
- kfree(s);
- goto out;
+ return seq_open_private(file, &igmp6_mcf_seq_ops,
+ sizeof(struct igmp6_mcf_iter_state));
}
-static struct file_operations igmp6_mcf_seq_fops = {
+static const struct file_operations igmp6_mcf_seq_fops = {
.owner = THIS_MODULE,
.open = igmp6_mcf_seq_open,
.read = seq_read,
np->hop_limit = 1;
#ifdef CONFIG_PROC_FS
- proc_net_fops_create("igmp6", S_IRUGO, &igmp6_mc_seq_fops);
- proc_net_fops_create("mcfilter6", S_IRUGO, &igmp6_mcf_seq_fops);
+ proc_net_fops_create(&init_net, "igmp6", S_IRUGO, &igmp6_mc_seq_fops);
+ proc_net_fops_create(&init_net, "mcfilter6", S_IRUGO, &igmp6_mcf_seq_fops);
#endif
return 0;
igmp6_socket = NULL; /* for safety */
#ifdef CONFIG_PROC_FS
- proc_net_remove("mcfilter6");
- proc_net_remove("igmp6");
+ proc_net_remove(&init_net, "mcfilter6");
+ proc_net_remove(&init_net, "igmp6");
#endif
}