Newer
Older
if (get_user(v,(int __user *)optval))
return -EFAULT;
init_net.ipv4.mroute_do_assert = (v) ? 1 : 0;
if (get_user(v,(int __user *)optval))
return -EFAULT;
if (v != init_net.ipv4.mroute_do_pim) {
init_net.ipv4.mroute_do_pim = v;
init_net.ipv4.mroute_do_assert = v;
if (init_net.ipv4.mroute_do_pim)
ret = inet_add_protocol(&pim_protocol,
IPPROTO_PIM);
else
ret = inet_del_protocol(&pim_protocol,
IPPROTO_PIM);
if (ret < 0)
ret = -EAGAIN;
/*
* Spurious command, or MRT_VERSION which you cannot
* set.
*/
default:
return -ENOPROTOOPT;
}
}
/*
* Getsock opt support for the multicast routing system.
*/
int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
#ifdef CONFIG_IP_PIMSM
optname!=MRT_PIM &&
#endif
optname!=MRT_ASSERT)
return -ENOPROTOOPT;
if (get_user(olr, optlen))
return -EFAULT;
olr = min_t(unsigned int, olr, sizeof(int));
if (olr < 0)
return -EINVAL;
if (optname == MRT_VERSION)
val = 0x0305;
val = init_net.ipv4.mroute_do_pim;
val = init_net.ipv4.mroute_do_assert;
return -EFAULT;
return 0;
}
/*
* The IP multicast ioctl support routines.
*/
int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
{
struct sioc_sg_req sr;
struct sioc_vif_req vr;
struct vif_device *vif;
struct mfc_cache *c;
if (vr.vifi >= init_net.ipv4.maxvif)
vif = &init_net.ipv4.vif_table[vr.vifi];
if (VIF_EXISTS(&init_net, vr.vifi)) {
vr.icount = vif->pkt_in;
vr.ocount = vif->pkt_out;
vr.ibytes = vif->bytes_in;
vr.obytes = vif->bytes_out;
return -EFAULT;
return 0;
}
read_unlock(&mrt_lock);
return -EADDRNOTAVAIL;
case SIOCGETSGCNT:
return -EFAULT;
read_lock(&mrt_lock);
c = ipmr_cache_find(sr.src.s_addr, sr.grp.s_addr);
if (c) {
sr.pktcnt = c->mfc_un.res.pkt;
sr.bytecnt = c->mfc_un.res.bytes;
sr.wrong_if = c->mfc_un.res.wrong_if;
return -EFAULT;
return 0;
}
read_unlock(&mrt_lock);
return -EADDRNOTAVAIL;
default:
return -ENOIOCTLCMD;
}
}
static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = ptr;
if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
v = &init_net.ipv4.vif_table[0];
for (ct = 0; ct < init_net.ipv4.maxvif; ct++, v++) {
static struct notifier_block ip_mr_notifier = {
.notifier_call = ipmr_device_event,
};
/*
* Encapsulate a packet by attaching a valid IPIP header to it.
* This avoids tunnel drivers and other mess and gives us the speed so
* important for multicast video.
*/
static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
struct iphdr *iph;
struct iphdr *old_iph = ip_hdr(skb);
skb_push(skb, sizeof(struct iphdr));

Arnaldo Carvalho de Melo
committed
skb->transport_header = skb->network_header;
skb_reset_network_header(skb);
iph = ip_hdr(skb);

Arnaldo Carvalho de Melo
committed
iph->tos = old_iph->tos;
iph->ttl = old_iph->ttl;
iph->frag_off = 0;
iph->daddr = daddr;
iph->saddr = saddr;
iph->protocol = IPPROTO_IPIP;
iph->ihl = 5;
iph->tot_len = htons(skb->len);
ip_select_ident(iph, skb->dst, NULL);
ip_send_check(iph);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
nf_reset(skb);
}
static inline int ipmr_forward_finish(struct sk_buff *skb)
{
struct ip_options * opt = &(IPCB(skb)->opt);
IP_INC_STATS_BH(dev_net(skb->dst->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
if (unlikely(opt->optlen))
ip_forward_options(skb);
return dst_output(skb);
}
/*
* Processing handlers for ipmr_forward
*/
static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
{
const struct iphdr *iph = ip_hdr(skb);
struct vif_device *vif = &init_net.ipv4.vif_table[vifi];
struct net_device *dev;
struct rtable *rt;
int encap = 0;
if (vif->dev == NULL)
goto out_free;
#ifdef CONFIG_IP_PIMSM
if (vif->flags & VIFF_REGISTER) {
vif->pkt_out++;
vif->dev->stats.tx_bytes += skb->len;
vif->dev->stats.tx_packets++;
ipmr_cache_report(skb, vifi, IGMPMSG_WHOLEPKT);
kfree_skb(skb);
return;
}
#endif
if (vif->flags&VIFF_TUNNEL) {
struct flowi fl = { .oif = vif->link,
.nl_u = { .ip4_u =
{ .daddr = vif->remote,
.saddr = vif->local,
.tos = RT_TOS(iph->tos) } },
.proto = IPPROTO_IPIP };
if (ip_route_output_key(&init_net, &rt, &fl))
goto out_free;
encap = sizeof(struct iphdr);
} else {
struct flowi fl = { .oif = vif->link,
.nl_u = { .ip4_u =
{ .daddr = iph->daddr,
.tos = RT_TOS(iph->tos) } },
.proto = IPPROTO_IPIP };
if (ip_route_output_key(&init_net, &rt, &fl))
goto out_free;
}
dev = rt->u.dst.dev;
if (skb->len+encap > dst_mtu(&rt->u.dst) && (ntohs(iph->frag_off) & IP_DF)) {
/* Do not fragment multicasts. Alas, IPv4 does not
allow to send ICMP, so that packets will disappear
to blackhole.
*/
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
ip_rt_put(rt);
goto out_free;
}
encap += LL_RESERVED_SPACE(dev) + rt->u.dst.header_len;
if (skb_cow(skb, encap)) {
dst_release(skb->dst);
skb->dst = &rt->u.dst;
ip_decrease_ttl(ip_hdr(skb));
/* FIXME: forward and output firewalls used to be called here.
* What do we do with netfilter? -- RR */
if (vif->flags & VIFF_TUNNEL) {
ip_encap(skb, vif->local, vif->remote);
/* FIXME: extra output firewall step used to be here. --RR */
vif->dev->stats.tx_packets++;
vif->dev->stats.tx_bytes += skb->len;
}
IPCB(skb)->flags |= IPSKB_FORWARDED;
/*
* RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
* not only before forwarding, but after forwarding on all output
* interfaces. It is clear, if mrouter runs a multicasting
* program, it should receive packets not depending to what interface
* program is joined.
* If we will not make it, the program will have to join on all
* interfaces. On the other hand, multihoming host (or router, but
* not mrouter) cannot join to more than one interface - it will
* result in receiving multiple packets.
*/
NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, dev,
ipmr_forward_finish);
return;
out_free:
kfree_skb(skb);
return;
}
static int ipmr_find_vif(struct net_device *dev)
{
int ct;
for (ct = init_net.ipv4.maxvif-1; ct >= 0; ct--) {
if (init_net.ipv4.vif_table[ct].dev == dev)
break;
}
return ct;
}
/* "local" means that we should preserve one skb (for local delivery) */
static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local)
{
int psend = -1;
int vif, ct;
vif = cache->mfc_parent;
cache->mfc_un.res.pkt++;
cache->mfc_un.res.bytes += skb->len;
/*
* Wrong interface: drop packet and (maybe) send PIM assert.
*/
if (init_net.ipv4.vif_table[vif].dev != skb->dev) {
if (skb->rtable->fl.iif == 0) {
/* It is our own packet, looped back.
Very complicated situation...
The best workaround until routing daemons will be
fixed is not to redistribute packet, if it was
send through wrong interface. It means, that
multicast applications WILL NOT work for
(S,G), which have default multicast route pointing
to wrong oif. In any case, it is not a good
idea to use multicasting applications on router.
*/
goto dont_forward;
}
cache->mfc_un.res.wrong_if++;
true_vifi = ipmr_find_vif(skb->dev);
if (true_vifi >= 0 && init_net.ipv4.mroute_do_assert &&
/* pimsm uses asserts, when switching from RPT to SPT,
so that we cannot check that packet arrived on an oif.
It is bad, but otherwise we would need to move pretty
large chunk of pimd to kernel. Ough... --ANK
*/
(init_net.ipv4.mroute_do_pim ||
cache->mfc_un.res.ttls[true_vifi] < 255) &&
cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
cache->mfc_un.res.last_assert = jiffies;
ipmr_cache_report(skb, true_vifi, IGMPMSG_WRONGVIF);
}
goto dont_forward;
}
init_net.ipv4.vif_table[vif].pkt_in++;
init_net.ipv4.vif_table[vif].bytes_in += skb->len;
/*
* Forward the frame
*/
for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
if (ip_hdr(skb)->ttl > cache->mfc_un.res.ttls[ct]) {
if (psend != -1) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2)
ipmr_queue_xmit(skb2, cache, psend);
}
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
}
}
if (psend != -1) {
if (local) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (skb2)
ipmr_queue_xmit(skb2, cache, psend);
} else {
ipmr_queue_xmit(skb, cache, psend);
return 0;
}
}
dont_forward:
if (!local)
kfree_skb(skb);
return 0;
}
/*
* Multicast packets for forwarding arrive here
*/
int ip_mr_input(struct sk_buff *skb)
{
struct mfc_cache *cache;
int local = skb->rtable->rt_flags&RTCF_LOCAL;
/* Packet is looped back after forward, it should not be
forwarded second time, but still can be delivered locally.
*/
if (IPCB(skb)->flags&IPSKB_FORWARDED)
goto dont_forward;
if (!local) {
if (IPCB(skb)->opt.router_alert) {
if (ip_call_ra_chain(skb))
return 0;
} else if (ip_hdr(skb)->protocol == IPPROTO_IGMP){
/* IGMPv1 (and broken IGMPv2 implementations sort of
Cisco IOS <= 11.2(8)) do not put router alert
option to IGMP packets destined to routable
groups. It is very bad, because it means
that we can forward NO IGMP messages.
*/
read_lock(&mrt_lock);
if (init_net.ipv4.mroute_sk) {
nf_reset(skb);
raw_rcv(init_net.ipv4.mroute_sk, skb);
read_unlock(&mrt_lock);
return 0;
}
read_unlock(&mrt_lock);
}
}
read_lock(&mrt_lock);
cache = ipmr_cache_find(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
int vif;
if (local) {
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
ip_local_deliver(skb);
if (skb2 == NULL) {
read_unlock(&mrt_lock);
return -ENOBUFS;
}
skb = skb2;
}
vif = ipmr_find_vif(skb->dev);
if (vif >= 0) {
int err = ipmr_cache_unresolved(vif, skb);
read_unlock(&mrt_lock);
return err;
}
read_unlock(&mrt_lock);
kfree_skb(skb);
return -ENODEV;
}
ip_mr_forward(skb, cache, local);
read_unlock(&mrt_lock);
if (local)
return ip_local_deliver(skb);
return 0;
dont_forward:
if (local)
return ip_local_deliver(skb);
kfree_skb(skb);
return 0;
}
#ifdef CONFIG_IP_PIMSM
static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen)
struct net_device *reg_dev = NULL;
struct iphdr *encap;
encap = (struct iphdr *)(skb_transport_header(skb) + pimlen);
/*
Check that:
a. packet is really destinted to a multicast group
b. packet is not a NULL-REGISTER
c. packet is not truncated
*/
if (!ipv4_is_multicast(encap->daddr) ||
ntohs(encap->tot_len) + pimlen > skb->len)
return 1;
read_lock(&mrt_lock);
if (reg_vif_num >= 0)
reg_dev = init_net.ipv4.vif_table[reg_vif_num].dev;
if (reg_dev)
dev_hold(reg_dev);
read_unlock(&mrt_lock);

Arnaldo Carvalho de Melo
committed
skb->mac_header = skb->network_header;
skb_reset_network_header(skb);
skb->dev = reg_dev;
skb->protocol = htons(ETH_P_IP);
skb->ip_summed = 0;
skb->pkt_type = PACKET_HOST;
dst_release(skb->dst);
skb->dst = NULL;
reg_dev->stats.rx_bytes += skb->len;
reg_dev->stats.rx_packets++;
nf_reset(skb);
netif_rx(skb);
dev_put(reg_dev);
}
#endif
#ifdef CONFIG_IP_PIMSM_V1
/*
* Handle IGMP messages of PIMv1
*/
int pim_rcv_v1(struct sk_buff * skb)
{
struct igmphdr *pim;
if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
goto drop;
pim = igmp_hdr(skb);
if (!init_net.ipv4.mroute_do_pim ||
pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
goto drop;
if (__pim_rcv(skb, sizeof(*pim))) {
drop:
kfree_skb(skb);
}
return 0;
}
#endif
#ifdef CONFIG_IP_PIMSM_V2
static int pim_rcv(struct sk_buff * skb)
{
struct pimreghdr *pim;
if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
pim = (struct pimreghdr *)skb_transport_header(skb);
if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
(ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
csum_fold(skb_checksum(skb, 0, skb->len, 0))))
if (__pim_rcv(skb, sizeof(*pim))) {
drop:
kfree_skb(skb);
}
return 0;
}
#endif
static int
ipmr_fill_mroute(struct sk_buff *skb, struct mfc_cache *c, struct rtmsg *rtm)
{
int ct;
struct rtnexthop *nhp;
struct net_device *dev = init_net.ipv4.vif_table[c->mfc_parent].dev;
u8 *b = skb_tail_pointer(skb);
struct rtattr *mp_head;
if (dev)
RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
if (c->mfc_un.res.ttls[ct] < 255) {
if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
goto rtattr_failure;
nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
nhp->rtnh_flags = 0;
nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
nhp->rtnh_ifindex = init_net.ipv4.vif_table[ct].dev->ifindex;
nhp->rtnh_len = sizeof(*nhp);
}
}
mp_head->rta_type = RTA_MULTIPATH;
mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
rtm->rtm_type = RTN_MULTICAST;
return 1;
rtattr_failure:
nlmsg_trim(skb, b);
return -EMSGSIZE;
}
int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait)
{
int err;
struct mfc_cache *cache;
struct rtable *rt = skb->rtable;
read_lock(&mrt_lock);
cache = ipmr_cache_find(rt->rt_src, rt->rt_dst);
struct iphdr *iph;
struct net_device *dev;
int vif;
if (nowait) {
read_unlock(&mrt_lock);
return -EAGAIN;
}
dev = skb->dev;
if (dev == NULL || (vif = ipmr_find_vif(dev)) < 0) {
read_unlock(&mrt_lock);
return -ENODEV;
}
skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2) {
read_unlock(&mrt_lock);
return -ENOMEM;
}
skb_push(skb2, sizeof(struct iphdr));
skb_reset_network_header(skb2);
iph = ip_hdr(skb2);
iph->ihl = sizeof(struct iphdr) >> 2;
iph->saddr = rt->rt_src;
iph->daddr = rt->rt_dst;
iph->version = 0;
err = ipmr_cache_unresolved(vif, skb2);
read_unlock(&mrt_lock);
return err;
}
if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
cache->mfc_flags |= MFC_NOTIFY;
err = ipmr_fill_mroute(skb, cache, rtm);
read_unlock(&mrt_lock);
return err;
}
/*
* The /proc interfaces to multicast routing /proc/ip_mr_cache /proc/ip_mr_vif
*/
struct ipmr_vif_iter {
int ct;
};
static struct vif_device *ipmr_vif_seq_idx(struct ipmr_vif_iter *iter,
loff_t pos)
{
for (iter->ct = 0; iter->ct < init_net.ipv4.maxvif; ++iter->ct) {
if (!VIF_EXISTS(&init_net, iter->ct))
return &init_net.ipv4.vif_table[iter->ct];
}
return NULL;
}
static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
return *pos ? ipmr_vif_seq_idx(seq->private, *pos - 1)
: SEQ_START_TOKEN;
}
static void *ipmr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct ipmr_vif_iter *iter = seq->private;
++*pos;
if (v == SEQ_START_TOKEN)
return ipmr_vif_seq_idx(iter, 0);
while (++iter->ct < init_net.ipv4.maxvif) {
if (!VIF_EXISTS(&init_net, iter->ct))
return &init_net.ipv4.vif_table[iter->ct];
}
return NULL;
}
static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
{
read_unlock(&mrt_lock);
}
static int ipmr_vif_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN) {
"Interface BytesIn PktsIn BytesOut PktsOut Flags Local Remote\n");
} else {
const struct vif_device *vif = v;
const char *name = vif->dev ? vif->dev->name : "none";
seq_printf(seq,
"%2Zd %-10s %8ld %7ld %8ld %7ld %05X %08X %08X\n",
vif - init_net.ipv4.vif_table,
name, vif->bytes_in, vif->pkt_in,
vif->bytes_out, vif->pkt_out,
vif->flags, vif->local, vif->remote);
}
return 0;
}
static const struct seq_operations ipmr_vif_seq_ops = {
.start = ipmr_vif_seq_start,
.next = ipmr_vif_seq_next,
.stop = ipmr_vif_seq_stop,
.show = ipmr_vif_seq_show,
};
static int ipmr_vif_open(struct inode *inode, struct file *file)
{
return seq_open_private(file, &ipmr_vif_seq_ops,
sizeof(struct ipmr_vif_iter));
static const struct file_operations ipmr_vif_fops = {
.owner = THIS_MODULE,
.open = ipmr_vif_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
struct ipmr_mfc_iter {
struct mfc_cache **cache;
int ct;
};
static struct mfc_cache *ipmr_mfc_seq_idx(struct ipmr_mfc_iter *it, loff_t pos)
{
struct mfc_cache *mfc;
it->cache = init_net.ipv4.mfc_cache_array;
for (it->ct = 0; it->ct < MFC_LINES; it->ct++)
for (mfc = init_net.ipv4.mfc_cache_array[it->ct];
mfc; mfc = mfc->next)
return mfc;
read_unlock(&mrt_lock);
it->cache = &mfc_unres_queue;
spin_lock_bh(&mfc_unres_lock);
if (pos-- == 0)
return mfc;
spin_unlock_bh(&mfc_unres_lock);
it->cache = NULL;
return NULL;
}
static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
{
struct ipmr_mfc_iter *it = seq->private;
it->cache = NULL;
it->ct = 0;
return *pos ? ipmr_mfc_seq_idx(seq->private, *pos - 1)
: SEQ_START_TOKEN;
}
static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct mfc_cache *mfc = v;
struct ipmr_mfc_iter *it = seq->private;
++*pos;
if (v == SEQ_START_TOKEN)
return ipmr_mfc_seq_idx(seq->private, 0);
if (mfc->next)
return mfc->next;
if (it->cache == &mfc_unres_queue)
BUG_ON(it->cache != init_net.ipv4.mfc_cache_array);
mfc = init_net.ipv4.mfc_cache_array[it->ct];
if (mfc)
return mfc;
}
/* exhausted cache_array, show unresolved */
read_unlock(&mrt_lock);
it->cache = &mfc_unres_queue;
it->ct = 0;
spin_lock_bh(&mfc_unres_lock);
mfc = mfc_unres_queue;
return mfc;
end_of_list:
spin_unlock_bh(&mfc_unres_lock);
it->cache = NULL;
return NULL;
}
static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
{
struct ipmr_mfc_iter *it = seq->private;
if (it->cache == &mfc_unres_queue)
spin_unlock_bh(&mfc_unres_lock);
else if (it->cache == init_net.ipv4.mfc_cache_array)
read_unlock(&mrt_lock);
}
static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
{
int n;
if (v == SEQ_START_TOKEN) {
"Group Origin Iif Pkts Bytes Wrong Oifs\n");
} else {
const struct mfc_cache *mfc = v;
const struct ipmr_mfc_iter *it = seq->private;
seq_printf(seq, "%08lX %08lX %-3hd",
(unsigned long) mfc->mfc_mcastgrp,
(unsigned long) mfc->mfc_origin,
seq_printf(seq, " %8lu %8lu %8lu",
mfc->mfc_un.res.pkt,
mfc->mfc_un.res.bytes,
mfc->mfc_un.res.wrong_if);
for (n = mfc->mfc_un.res.minvif;
n < mfc->mfc_un.res.maxvif; n++ ) {
if (VIF_EXISTS(&init_net, n) &&
mfc->mfc_un.res.ttls[n] < 255)
seq_printf(seq,
} else {
/* unresolved mfc_caches don't contain
* pkt, bytes and wrong_if values
*/
seq_printf(seq, " %8lu %8lu %8lu", 0ul, 0ul, 0ul);
}
seq_putc(seq, '\n');
}
return 0;
}
static const struct seq_operations ipmr_mfc_seq_ops = {
.start = ipmr_mfc_seq_start,
.next = ipmr_mfc_seq_next,
.stop = ipmr_mfc_seq_stop,
.show = ipmr_mfc_seq_show,
};
static int ipmr_mfc_open(struct inode *inode, struct file *file)
{
return seq_open_private(file, &ipmr_mfc_seq_ops,
sizeof(struct ipmr_mfc_iter));
static const struct file_operations ipmr_mfc_fops = {
.owner = THIS_MODULE,
.open = ipmr_mfc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
#ifdef CONFIG_IP_PIMSM_V2
static struct net_protocol pim_protocol = {
.handler = pim_rcv,
};
#endif
/*
* Setup for IP multicast routing
*/
static int __net_init ipmr_net_init(struct net *net)
{
int err = 0;
net->ipv4.vif_table = kcalloc(MAXVIFS, sizeof(struct vif_device),
GFP_KERNEL);
if (!net->ipv4.vif_table) {
err = -ENOMEM;
goto fail;
}
/* Forwarding cache */
net->ipv4.mfc_cache_array = kcalloc(MFC_LINES,
sizeof(struct mfc_cache *),
GFP_KERNEL);
if (!net->ipv4.mfc_cache_array) {
err = -ENOMEM;
goto fail_mfc_cache;
}
return 0;
fail_mfc_cache:
kfree(net->ipv4.vif_table);
fail:
return err;
}
static void __net_exit ipmr_net_exit(struct net *net)
{
kfree(net->ipv4.mfc_cache_array);
kfree(net->ipv4.vif_table);
}
static struct pernet_operations ipmr_net_ops = {
.init = ipmr_net_init,
.exit = ipmr_net_exit,
};
mrt_cachep = kmem_cache_create("ip_mrt_cache",
sizeof(struct mfc_cache),
err = register_pernet_subsys(&ipmr_net_ops);
if (err)
goto reg_pernet_fail;
setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0);
err = register_netdevice_notifier(&ip_mr_notifier);
if (err)
goto reg_notif_fail;
err = -ENOMEM;
if (!proc_net_fops_create(&init_net, "ip_mr_vif", 0, &ipmr_vif_fops))
goto proc_vif_fail;
if (!proc_net_fops_create(&init_net, "ip_mr_cache", 0, &ipmr_mfc_fops))
goto proc_cache_fail;
return 0;
#ifdef CONFIG_PROC_FS
proc_cache_fail:
proc_net_remove(&init_net, "ip_mr_vif");
proc_vif_fail:
unregister_netdevice_notifier(&ip_mr_notifier);
reg_notif_fail:
del_timer(&ipmr_expire_timer);
unregister_pernet_subsys(&ipmr_net_ops);
reg_pernet_fail: