* Authors:
* 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
*
* This program is free software; you can redistribute it and/or
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
+#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <net/ndisc.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
+#include <net/inet_common.h>
#include <net/ip6_checksum.h>
/* Big mc list lock for all the sockets */
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);
-
static void igmp6_join_group(struct ifmcaddr6 *ma);
static void igmp6_leave_group(struct ifmcaddr6 *ma);
static void igmp6_timer_handler(unsigned long data);
#define IGMP6_UNSOLICITED_IVAL (10*HZ)
#define MLD_QRV_DEFAULT 2
-#define MLD_V1_SEEN(idev) (ipv6_devconf.force_mld_version == 1 || \
+#define MLD_V1_SEEN(idev) (dev_net((idev)->dev)->ipv6.devconf_all->force_mld_version == 1 || \
(idev)->cnf.force_mld_version == 1 || \
((idev)->mc_v1_seen && \
time_before(jiffies, (idev)->mc_v1_seen)))
((MLDV2_MASK(value, nbmant) | (1<<(nbmant))) << \
(MLDV2_MASK((value) >> (nbmant), nbexp) + (nbexp))))
-#define MLDV2_QQIC(value) MLDV2_EXP(0x80, 4, 3, value)
#define MLDV2_MRC(value) MLDV2_EXP(0x8000, 12, 3, value)
#define IPV6_MLD_MAX_MSF 64
* socket join on multicast group
*/
-int ipv6_sock_mc_join(struct sock *sk, int ifindex, struct in6_addr *addr)
+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;
if (!ipv6_addr_is_multicast(addr))
if (ifindex == 0) {
struct rt6_info *rt;
- rt = rt6_lookup(addr, NULL, 0, 0);
+ rt = rt6_lookup(net, addr, NULL, 0, 0);
if (rt) {
dev = rt->rt6i_dev;
dev_hold(dev);
dst_release(&rt->u.dst);
}
} else
- dev = dev_get_by_index(ifindex);
+ dev = dev_get_by_index(net, ifindex);
if (dev == NULL) {
sock_kfree_s(sk, mc_lst, sizeof(*mc_lst));
/*
* socket leave on multicast group
*/
-int ipv6_sock_mc_drop(struct sock *sk, int ifindex, struct in6_addr *addr)
+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, **lnk;
+ struct net *net = sock_net(sk);
write_lock_bh(&ipv6_sk_mc_lock);
for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
*lnk = mc_lst->next;
write_unlock_bh(&ipv6_sk_mc_lock);
- if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) {
+ dev = dev_get_by_index(net, mc_lst->ifindex);
+ if (dev != NULL) {
struct inet6_dev *idev = in6_dev_get(dev);
(void) ip6_mc_leave_src(sk, mc_lst, idev);
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 inet6_dev *idev = NULL;
if (ifindex == 0) {
struct rt6_info *rt;
- rt = rt6_lookup(group, NULL, 0, 0);
+ rt = rt6_lookup(net, group, NULL, 0, 0);
if (rt) {
dev = rt->rt6i_dev;
dev_hold(dev);
dst_release(&rt->u.dst);
}
} else
- dev = dev_get_by_index(ifindex);
+ dev = dev_get_by_index(net, ifindex);
if (!dev)
- return NULL;
+ goto nodev;
idev = in6_dev_get(dev);
- if (!idev) {
- dev_put(dev);
- return NULL;
- }
+ if (!idev)
+ goto release;
read_lock_bh(&idev->lock);
- if (idev->dead) {
- read_unlock_bh(&idev->lock);
- in6_dev_put(idev);
- dev_put(dev);
- return NULL;
- }
+ if (idev->dead)
+ goto unlock_release;
+
return idev;
+
+unlock_release:
+ read_unlock_bh(&idev->lock);
+ in6_dev_put(idev);
+release:
+ dev_put(dev);
+nodev:
+ return NULL;
}
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);
write_lock_bh(&ipv6_sk_mc_lock);
while ((mc_lst = np->ipv6_mc_list) != 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(net, mc_lst->ifindex);
if (dev) {
struct inet6_dev *idev = in6_dev_get(dev);
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;
- if (pgsr->gsr_group.ss_family != AF_INET6 ||
- pgsr->gsr_source.ss_family != AF_INET6)
- return -EINVAL;
-
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;
- idev = ip6_mc_find_dev(group, pgsr->gsr_interface);
+ idev = ip6_mc_find_dev(net, group, pgsr->gsr_interface);
if (!idev)
return -ENODEV;
dev = idev->dev;
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;
gsf->gf_fmode != MCAST_EXCLUDE)
return -EINVAL;
- idev = ip6_mc_find_dev(group, gsf->gf_interface);
+ idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
if (!idev)
return -ENODEV;
struct net_device *dev;
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;
- idev = ip6_mc_find_dev(group, gsf->gf_interface);
+ idev = ip6_mc_find_dev(net, group, gsf->gf_interface);
if (!idev)
return -ENODEV;
return err;
}
-int inet6_mc_check(struct sock *sk, struct in6_addr *mc_addr,
- struct in6_addr *src_addr)
+int 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;
/*
* device multicast group inc (add if not found)
*/
-int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
+int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr)
{
struct ifmcaddr6 *mc;
struct inet6_dev *idev;
return -ENOMEM;
}
- init_timer(&mc->mca_timer);
- mc->mca_timer.function = igmp6_timer_handler;
- mc->mca_timer.data = (unsigned long) mc;
+ setup_timer(&mc->mca_timer, igmp6_timer_handler, (unsigned long)mc);
ipv6_addr_copy(&mc->mca_addr, addr);
mc->idev = idev;
/*
* device multicast group del
*/
-int __ipv6_dev_mc_dec(struct inet6_dev *idev, struct in6_addr *addr)
+int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
{
struct ifmcaddr6 *ma, **map;
return -ENOENT;
}
-int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr)
+int ipv6_dev_mc_dec(struct net_device *dev, const struct in6_addr *addr)
{
struct inet6_dev *idev = in6_dev_get(dev);
int err;
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:
/*
* check if the interface/address pair is valid
*/
-int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group,
- struct in6_addr *src_addr)
+int ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group,
+ const struct in6_addr *src_addr)
{
struct inet6_dev *idev;
struct ifmcaddr6 *mc;
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);
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;
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);
+ addr_type = ipv6_addr_type(&ipv6_hdr(skb)->saddr);
if (addr_type != IPV6_ADDR_ANY &&
!(addr_type&IPV6_ADDR_LINKLOCAL))
return -EINVAL;
static struct sk_buff *mld_newpack(struct net_device *dev, int size)
{
- struct sock *sk = igmp6_socket->sk;
+ struct net *net = dev_net(dev);
+ struct sock *sk = net->ipv6.igmp_sk;
struct sk_buff *skb;
struct mld2_report *pmr;
struct in6_addr addr_buf;
+ const struct in6_addr *saddr;
int err;
u8 ra[8] = { IPPROTO_ICMPV6, 0,
IPV6_TLV_ROUTERALERT, 2, 0, 0,
IPV6_TLV_PADN, 0 };
/* we assume size > sizeof(ra) here */
- skb = sock_alloc_send_skb(sk, size + LL_RESERVED_SPACE(dev), 1, &err);
+ skb = sock_alloc_send_skb(sk, size + LL_ALLOCATED_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
* when a valid link-local address is not available.
*/
- memset(&addr_buf, 0, sizeof(addr_buf));
- }
+ saddr = &in6addr_any;
+ } else
+ saddr = &addr_buf;
- ip6_nd_hdr(sk, skb, dev, &addr_buf, &mld2_all_mcr, NEXTHDR_HOP, 0);
+ ip6_nd_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0);
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;
return skb;
}
-static inline int mld_dev_queue_xmit2(struct sk_buff *skb)
-{
- struct net_device *dev = skb->dev;
-
- 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;
- }
- }
- 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);
-}
-
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);
+ struct net *net = dev_net(skb->dev);
int err;
+ struct flowi fl;
+ struct dst_entry *dst;
- IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
- payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
- sizeof(struct ipv6hdr);
- mldlen = skb->tail - skb->h.raw;
+ IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
+
+ 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));
- err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
- mld_dev_queue_xmit);
+ IPPROTO_ICMPV6, csum_partial(skb_transport_header(skb),
+ mldlen, 0));
+
+ dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
+
+ if (!dst) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ icmpv6_flow_init(net->ipv6.igmp_sk, &fl, ICMPV6_MLD2_REPORT,
+ &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
+ skb->dev->ifindex);
+
+ err = xfrm_lookup(net, &dst, &fl, NULL, 0);
+ skb_dst_set(skb, dst);
+ if (err)
+ goto err_out;
+
+ payload_len = skb->len;
+
+ err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
+ dst_output);
+out:
if (!err) {
- ICMP6_INC_STATS(idev,ICMP6_MIB_OUTMSGS);
- IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
+ ICMP6MSGOUT_INC_STATS_BH(net, idev, ICMPV6_MLD2_REPORT);
+ ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTMSGS);
+ IP6_UPD_PO_STATS_BH(net, idev, IPSTATS_MIB_OUTMCAST, payload_len);
} else
- IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
+ IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_OUTDISCARDS);
if (likely(idev != NULL))
in6_dev_put(idev);
+ return;
+
+err_out:
+ kfree_skb(skb);
+ goto out;
}
static int grec_size(struct ifmcaddr6 *pmc, int type, int gdel, int sdel)
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) {
static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
{
- struct sock *sk = igmp6_socket->sk;
+ struct net *net = dev_net(dev);
+ struct sock *sk = net->ipv6.igmp_sk;
struct inet6_dev *idev;
struct sk_buff *skb;
struct icmp6hdr *hdr;
- struct in6_addr *snd_addr;
+ const struct in6_addr *snd_addr, *saddr;
struct in6_addr *addrp;
struct in6_addr addr_buf;
- struct in6_addr all_routers;
int err, len, payload_len, full_len;
u8 ra[8] = { IPPROTO_ICMPV6, 0,
IPV6_TLV_ROUTERALERT, 2, 0, 0,
IPV6_TLV_PADN, 0 };
+ struct flowi fl;
+ struct dst_entry *dst;
- 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;
- ipv6_addr_all_routers(&all_routers);
- }
+ if (type == ICMPV6_MGM_REDUCTION)
+ snd_addr = &in6addr_linklocal_allrouters;
+ else
+ snd_addr = addr;
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
payload_len = len + sizeof(ra);
full_len = sizeof(struct ipv6hdr) + payload_len;
- skb = sock_alloc_send_skb(sk, LL_RESERVED_SPACE(dev) + full_len, 1, &err);
+ rcu_read_lock();
+ IP6_UPD_PO_STATS(net, __in6_dev_get(dev),
+ IPSTATS_MIB_OUT, full_len);
+ rcu_read_unlock();
+
+ skb = sock_alloc_send_skb(sk, LL_ALLOCATED_SPACE(dev) + full_len, 1, &err);
if (skb == NULL) {
rcu_read_lock();
- IP6_INC_STATS(__in6_dev_get(dev),
+ IP6_INC_STATS(net, __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
* when a valid link-local address is not available.
*/
- memset(&addr_buf, 0, sizeof(addr_buf));
- }
+ saddr = &in6addr_any;
+ } else
+ saddr = &addr_buf;
- ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len);
+ ip6_nd_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len);
memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
ipv6_addr_copy(addrp, addr);
- hdr->icmp6_cksum = csum_ipv6_magic(&addr_buf, snd_addr, len,
+ hdr->icmp6_cksum = csum_ipv6_magic(saddr, snd_addr, len,
IPPROTO_ICMPV6,
- csum_partial((__u8 *) hdr, len, 0));
+ csum_partial(hdr, len, 0));
idev = in6_dev_get(skb->dev);
- err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
- mld_dev_queue_xmit);
+ dst = icmp6_dst_alloc(skb->dev, NULL, &ipv6_hdr(skb)->daddr);
+ if (!dst) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ icmpv6_flow_init(sk, &fl, type,
+ &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr,
+ skb->dev->ifindex);
+
+ err = xfrm_lookup(net, &dst, &fl, NULL, 0);
+ if (err)
+ goto err_out;
+
+ skb_dst_set(skb, dst);
+ err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev,
+ dst_output);
+out:
if (!err) {
- if (type == ICMPV6_MGM_REDUCTION)
- ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBREDUCTIONS);
- else
- ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBRESPONSES);
- ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
- IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
+ ICMP6MSGOUT_INC_STATS(net, idev, type);
+ ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
+ IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUTMCAST, full_len);
} else
- IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
+ IP6_INC_STATS(net, idev, IPSTATS_MIB_OUTDISCARDS);
if (likely(idev != NULL))
in6_dev_put(idev);
return;
+
+err_out:
+ kfree_skb(skb);
+ goto out;
}
static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
for (j=0; j<i; j++)
(void) ip6_mc_del1_src(pmc, sfmode, &psfsrc[i]);
} else if (isexclude != (pmc->mca_sfcount[MCAST_EXCLUDE] != 0)) {
- struct inet6_dev *idev = pmc->idev;
struct ip6_sf_list *psf;
/* filter mode change */
/* 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);
}
write_lock_bh(&idev->lock);
rwlock_init(&idev->mc_lock);
idev->mc_gq_running = 0;
- init_timer(&idev->mc_gq_timer);
- idev->mc_gq_timer.data = (unsigned long) idev;
- idev->mc_gq_timer.function = &mld_gq_timer_expire;
+ setup_timer(&idev->mc_gq_timer, mld_gq_timer_expire,
+ (unsigned long)idev);
idev->mc_tomb = NULL;
idev->mc_ifc_count = 0;
- init_timer(&idev->mc_ifc_timer);
- idev->mc_ifc_timer.data = (unsigned long) idev;
- idev->mc_ifc_timer.function = &mld_ifc_timer_expire;
+ setup_timer(&idev->mc_ifc_timer, mld_ifc_timer_expire,
+ (unsigned long)idev);
idev->mc_qrv = MLD_QRV_DEFAULT;
idev->mc_maxdelay = IGMP6_UNSOLICITED_IVAL;
idev->mc_v1_seen = 0;
void ipv6_mc_destroy_dev(struct inet6_dev *idev)
{
struct ifmcaddr6 *i;
- struct in6_addr maddr;
/* Deactivate timers */
ipv6_mc_down(idev);
/* Delete all-nodes address. */
- ipv6_addr_all_nodes(&maddr);
-
/* We cannot call ipv6_dev_mc_dec() directly, our caller in
* addrconf.c has NULL'd out dev->ip6_ptr so in6_dev_get() will
* fail.
*/
- __ipv6_dev_mc_dec(idev, &maddr);
+ __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allnodes);
- if (idev->cnf.forwarding) {
- ipv6_addr_all_routers(&maddr);
- __ipv6_dev_mc_dec(idev, &maddr);
- }
+ if (idev->cnf.forwarding)
+ __ipv6_dev_mc_dec(idev, &in6addr_linklocal_allrouters);
write_lock_bh(&idev->lock);
while ((i = idev->mc_list) != NULL) {
#ifdef CONFIG_PROC_FS
struct igmp6_mc_iter_state {
+ struct seq_net_private p;
struct net_device *dev;
struct inet6_dev *idev;
};
{
struct ifmcaddr6 *im = NULL;
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
+ struct net *net = seq_file_net(seq);
- for (state->dev = dev_base, state->idev = NULL;
- state->dev;
- state->dev = state->dev->next) {
+ state->idev = NULL;
+ for_each_netdev(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;
}
static void *igmp6_mc_seq_start(struct seq_file *seq, loff_t *pos)
+ __acquires(dev_base_lock)
{
read_lock(&dev_base_lock);
return igmp6_mc_get_idx(seq, *pos);
}
static void igmp6_mc_seq_stop(struct seq_file *seq, void *v)
+ __releases(dev_base_lock)
{
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
if (likely(state->idev != NULL)) {
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
seq_printf(seq,
- "%-4d %-15s " NIP6_SEQFMT " %5d %08X %ld\n",
+ "%-4d %-15s %pi6 %5d %08X %ld\n",
state->dev->ifindex, state->dev->name,
- NIP6(im->mca_addr),
+ &im->mca_addr,
im->mca_users, im->mca_flags,
(im->mca_flags&MAF_TIMER_RUNNING) ?
jiffies_to_clock_t(im->mca_timer.expires-jiffies) : 0);
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 = kzalloc(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;
-out:
- return rc;
-out_kfree:
- kfree(s);
- goto out;
+ return seq_open_net(inode, file, &igmp6_mc_seq_ops,
+ sizeof(struct igmp6_mc_iter_state));
}
static const struct file_operations igmp6_mc_seq_fops = {
.open = igmp6_mc_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
struct igmp6_mcf_iter_state {
+ struct seq_net_private p;
struct net_device *dev;
struct inet6_dev *idev;
struct ifmcaddr6 *im;
struct ip6_sf_list *psf = NULL;
struct ifmcaddr6 *im = NULL;
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
+ struct net *net = seq_file_net(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(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;
}
static void *igmp6_mcf_seq_start(struct seq_file *seq, loff_t *pos)
+ __acquires(dev_base_lock)
{
read_lock(&dev_base_lock);
return *pos ? igmp6_mcf_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;
}
static void igmp6_mcf_seq_stop(struct seq_file *seq, void *v)
+ __releases(dev_base_lock)
{
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
if (likely(state->im != NULL)) {
"Source Address", "INC", "EXC");
} else {
seq_printf(seq,
- "%3d %6.6s " NIP6_SEQFMT " " NIP6_SEQFMT " %6lu %6lu\n",
+ "%3d %6.6s %pi6 %pi6 %6lu %6lu\n",
state->dev->ifindex, state->dev->name,
- NIP6(state->im->mca_addr),
- NIP6(psf->sf_addr),
+ &state->im->mca_addr,
+ &psf->sf_addr,
psf->sf_count[MCAST_INCLUDE],
psf->sf_count[MCAST_EXCLUDE]);
}
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 = kzalloc(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;
-out:
- return rc;
-out_kfree:
- kfree(s);
- goto out;
+ return seq_open_net(inode, file, &igmp6_mcf_seq_ops,
+ sizeof(struct igmp6_mcf_iter_state));
}
static const struct file_operations igmp6_mcf_seq_fops = {
.open = igmp6_mcf_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release_private,
+ .release = seq_release_net,
};
+
+static int igmp6_proc_init(struct net *net)
+{
+ int err;
+
+ err = -ENOMEM;
+ if (!proc_net_fops_create(net, "igmp6", S_IRUGO, &igmp6_mc_seq_fops))
+ goto out;
+ if (!proc_net_fops_create(net, "mcfilter6", S_IRUGO,
+ &igmp6_mcf_seq_fops))
+ goto out_proc_net_igmp6;
+
+ err = 0;
+out:
+ return err;
+
+out_proc_net_igmp6:
+ proc_net_remove(net, "igmp6");
+ goto out;
+}
+
+static void igmp6_proc_exit(struct net *net)
+{
+ proc_net_remove(net, "mcfilter6");
+ proc_net_remove(net, "igmp6");
+}
+#else
+static int igmp6_proc_init(struct net *net)
+{
+ return 0;
+}
+static void igmp6_proc_exit(struct net *net)
+{
+ ;
+}
#endif
-int __init igmp6_init(struct net_proto_family *ops)
+static int igmp6_net_init(struct net *net)
{
- struct ipv6_pinfo *np;
- struct sock *sk;
int err;
- err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &igmp6_socket);
+ err = inet_ctl_sock_create(&net->ipv6.igmp_sk, PF_INET6,
+ SOCK_RAW, IPPROTO_ICMPV6, net);
if (err < 0) {
printk(KERN_ERR
"Failed to initialize the IGMP6 control socket (err %d).\n",
err);
- igmp6_socket = NULL; /* For safety. */
- return err;
+ goto out;
}
- sk = igmp6_socket->sk;
- sk->sk_allocation = GFP_ATOMIC;
- sk->sk_prot->unhash(sk);
+ inet6_sk(net->ipv6.igmp_sk)->hop_limit = 1;
- np = inet6_sk(sk);
- np->hop_limit = 1;
+ err = igmp6_proc_init(net);
+ if (err)
+ goto out_sock_create;
+out:
+ return err;
-#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);
-#endif
+out_sock_create:
+ inet_ctl_sock_destroy(net->ipv6.igmp_sk);
+ goto out;
+}
- return 0;
+static void igmp6_net_exit(struct net *net)
+{
+ inet_ctl_sock_destroy(net->ipv6.igmp_sk);
+ igmp6_proc_exit(net);
}
-void igmp6_cleanup(void)
+static struct pernet_operations igmp6_net_ops = {
+ .init = igmp6_net_init,
+ .exit = igmp6_net_exit,
+};
+
+int __init igmp6_init(void)
{
- sock_release(igmp6_socket);
- igmp6_socket = NULL; /* for safety */
+ return register_pernet_subsys(&igmp6_net_ops);
+}
-#ifdef CONFIG_PROC_FS
- proc_net_remove("mcfilter6");
- proc_net_remove("igmp6");
-#endif
+void igmp6_cleanup(void)
+{
+ unregister_pernet_subsys(&igmp6_net_ops);
}