/*
* IP multicast routing support for mrouted 3.6/3.8
*
- * (c) 1995 Alan Cox, <alan@redhat.com>
+ * (c) 1995 Alan Cox, <alan@lxorguk.ukuu.org.uk>
* Linux Consultancy and Custom Driver Development
*
* This program is free software; you can redistribute it and/or
/* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
+static void ipmr_del_tunnel(struct net_device *dev, struct vifctl *v)
+{
+ dev_close(dev);
+
+ dev = __dev_get_by_name(&init_net, "tunl0");
+ if (dev) {
+ const struct net_device_ops *ops = dev->netdev_ops;
+ struct ifreq ifr;
+ struct ip_tunnel_parm p;
+
+ memset(&p, 0, sizeof(p));
+ p.iph.daddr = v->vifc_rmt_addr.s_addr;
+ p.iph.saddr = v->vifc_lcl_addr.s_addr;
+ p.iph.version = 4;
+ p.iph.ihl = 5;
+ p.iph.protocol = IPPROTO_IPIP;
+ sprintf(p.name, "dvmrp%d", v->vifc_vifi);
+ ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
+
+ if (ops->ndo_do_ioctl) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(KERNEL_DS);
+ ops->ndo_do_ioctl(dev, &ifr, SIOCDELTUNNEL);
+ set_fs(oldfs);
+ }
+ }
+}
+
static
struct net_device *ipmr_new_tunnel(struct vifctl *v)
{
dev = __dev_get_by_name(&init_net, "tunl0");
if (dev) {
+ const struct net_device_ops *ops = dev->netdev_ops;
int err;
struct ifreq ifr;
- mm_segment_t oldfs;
struct ip_tunnel_parm p;
struct in_device *in_dev;
sprintf(p.name, "dvmrp%d", v->vifc_vifi);
ifr.ifr_ifru.ifru_data = (__force void __user *)&p;
- oldfs = get_fs(); set_fs(KERNEL_DS);
- err = dev->do_ioctl(dev, &ifr, SIOCADDTUNNEL);
- set_fs(oldfs);
+ if (ops->ndo_do_ioctl) {
+ mm_segment_t oldfs = get_fs();
+
+ set_fs(KERNEL_DS);
+ err = ops->ndo_do_ioctl(dev, &ifr, SIOCADDTUNNEL);
+ set_fs(oldfs);
+ } else
+ err = -EOPNOTSUPP;
dev = NULL;
if (dev_open(dev))
goto failure;
+ dev_hold(dev);
}
}
return dev;
if (dev_open(dev))
goto failure;
+ dev_hold(dev);
+
return dev;
failure:
/*
* Delete a VIF entry
+ * @notify: Set to 1, if the caller is a notifier_call
*/
-static int vif_delete(int vifi)
+static int vif_delete(int vifi, int notify)
{
struct vif_device *v;
struct net_device *dev;
ip_rt_multicast_event(in_dev);
}
- if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER))
+ if (v->flags&(VIFF_TUNNEL|VIFF_REGISTER) && !notify)
unregister_netdevice(dev);
dev_put(dev);
atomic_dec(&cache_resolve_queue_len);
- while ((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) {
+ while ((skb = skb_dequeue(&c->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
nlh->nlmsg_type = NLMSG_ERROR;
struct vif_device *v = &vif_table[vifi];
struct net_device *dev;
struct in_device *in_dev;
+ int err;
/* Is vif busy ? */
if (VIF_EXISTS(vifi))
dev = ipmr_reg_vif();
if (!dev)
return -ENOBUFS;
+ err = dev_set_allmulti(dev, 1);
+ if (err) {
+ unregister_netdevice(dev);
+ dev_put(dev);
+ return err;
+ }
break;
#endif
case VIFF_TUNNEL:
dev = ipmr_new_tunnel(vifc);
if (!dev)
return -ENOBUFS;
+ err = dev_set_allmulti(dev, 1);
+ if (err) {
+ ipmr_del_tunnel(dev, vifc);
+ dev_put(dev);
+ return err;
+ }
break;
case 0:
dev = ip_dev_find(&init_net, vifc->vifc_lcl_addr.s_addr);
if (!dev)
return -EADDRNOTAVAIL;
- dev_put(dev);
+ err = dev_set_allmulti(dev, 1);
+ if (err) {
+ dev_put(dev);
+ return err;
+ }
break;
default:
return -EINVAL;
if ((in_dev = __in_dev_get_rtnl(dev)) == NULL)
return -EADDRNOTAVAIL;
IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
- dev_set_allmulti(dev, +1);
ip_rt_multicast_event(in_dev);
/*
* Fill in the VIF structures
*/
- v->rate_limit=vifc->vifc_rate_limit;
- v->local=vifc->vifc_lcl_addr.s_addr;
- v->remote=vifc->vifc_rmt_addr.s_addr;
- v->flags=vifc->vifc_flags;
+ v->rate_limit = vifc->vifc_rate_limit;
+ v->local = vifc->vifc_lcl_addr.s_addr;
+ v->remote = vifc->vifc_rmt_addr.s_addr;
+ v->flags = vifc->vifc_flags;
if (!mrtsock)
v->flags |= VIFF_STATIC;
- v->threshold=vifc->vifc_threshold;
+ v->threshold = vifc->vifc_threshold;
v->bytes_in = 0;
v->bytes_out = 0;
v->pkt_in = 0;
/* And finish update writing critical data */
write_lock_bh(&mrt_lock);
- dev_hold(dev);
- v->dev=dev;
+ v->dev = dev;
#ifdef CONFIG_IP_PIMSM
if (v->flags&VIFF_REGISTER)
reg_vif_num = vifi;
static struct mfc_cache *ipmr_cache_find(__be32 origin, __be32 mcastgrp)
{
- int line=MFC_HASH(mcastgrp,origin);
+ int line = MFC_HASH(mcastgrp, origin);
struct mfc_cache *c;
for (c=mfc_cache_array[line]; c; c = c->next) {
*/
static struct mfc_cache *ipmr_cache_alloc(void)
{
- struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
- if (c==NULL)
+ struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_KERNEL);
+ if (c == NULL)
return NULL;
c->mfc_un.res.minvif = MAXVIFS;
return c;
static struct mfc_cache *ipmr_cache_alloc_unres(void)
{
- struct mfc_cache *c=kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
- if (c==NULL)
+ struct mfc_cache *c = kmem_cache_zalloc(mrt_cachep, GFP_ATOMIC);
+ if (c == NULL)
return NULL;
skb_queue_head_init(&c->mfc_un.unres.unresolved);
c->mfc_un.unres.expires = jiffies + 10*HZ;
* Play the pending entries through our router
*/
- while ((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) {
+ while ((skb = __skb_dequeue(&uc->mfc_un.unres.unresolved))) {
if (ip_hdr(skb)->version == 0) {
struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct iphdr));
* Add our header
*/
- igmp=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
+ igmp=(struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
igmp->type =
msg->im_msgtype = assert;
igmp->code = 0;
/*
* Deliver to mrouted
*/
- if ((ret=sock_queue_rcv_skb(mroute_socket,skb))<0) {
+ if ((ret = sock_queue_rcv_skb(mroute_socket, skb))<0) {
if (net_ratelimit())
printk(KERN_WARNING "mroute: pending queue full, dropping entries.\n");
kfree_skb(skb);
* Create a new entry if allowable
*/
- if (atomic_read(&cache_resolve_queue_len)>=10 ||
+ if (atomic_read(&cache_resolve_queue_len) >= 10 ||
(c=ipmr_cache_alloc_unres())==NULL) {
spin_unlock_bh(&mfc_unres_lock);
kfree_skb(skb);
err = -ENOBUFS;
} else {
- skb_queue_tail(&c->mfc_un.unres.unresolved,skb);
+ skb_queue_tail(&c->mfc_un.unres.unresolved, skb);
err = 0;
}
int line;
struct mfc_cache *c, **cp;
- line=MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
+ line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
int line;
struct mfc_cache *uc, *c, **cp;
- line=MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
+ line = MFC_HASH(mfc->mfcc_mcastgrp.s_addr, mfc->mfcc_origin.s_addr);
for (cp=&mfc_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
if (!ipv4_is_multicast(mfc->mfcc_mcastgrp.s_addr))
return -EINVAL;
- c=ipmr_cache_alloc();
- if (c==NULL)
+ c = ipmr_cache_alloc();
+ if (c == NULL)
return -ENOMEM;
- c->mfc_origin=mfc->mfcc_origin.s_addr;
- c->mfc_mcastgrp=mfc->mfcc_mcastgrp.s_addr;
- c->mfc_parent=mfc->mfcc_parent;
+ c->mfc_origin = mfc->mfcc_origin.s_addr;
+ c->mfc_mcastgrp = mfc->mfcc_mcastgrp.s_addr;
+ c->mfc_parent = mfc->mfcc_parent;
ipmr_update_thresholds(c, mfc->mfcc_ttls);
if (!mrtsock)
c->mfc_flags |= MFC_STATIC;
*/
for (i=0; i<maxvif; i++) {
if (!(vif_table[i].flags&VIFF_STATIC))
- vif_delete(i);
+ vif_delete(i, 0);
}
/*
* Wipe the cache
*/
- for (i=0;i<MFC_LINES;i++) {
+ for (i=0; i<MFC_LINES; i++) {
struct mfc_cache *c, **cp;
cp = &mfc_cache_array[i];
IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)--;
write_lock_bh(&mrt_lock);
- mroute_socket=NULL;
+ mroute_socket = NULL;
write_unlock_bh(&mrt_lock);
mroute_clean_tables(sk);
* MOSPF/PIM router set up we can clean this up.
*/
-int ip_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int optlen)
+int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, int optlen)
{
int ret;
struct vifctl vif;
if (sk->sk_type != SOCK_RAW ||
inet_sk(sk)->num != IPPROTO_IGMP)
return -EOPNOTSUPP;
- if (optlen!=sizeof(int))
+ if (optlen != sizeof(int))
return -ENOPROTOOPT;
rtnl_lock();
ret = ip_ra_control(sk, 1, mrtsock_destruct);
if (ret == 0) {
write_lock_bh(&mrt_lock);
- mroute_socket=sk;
+ mroute_socket = sk;
write_unlock_bh(&mrt_lock);
IPV4_DEVCONF_ALL(sock_net(sk), MC_FORWARDING)++;
rtnl_unlock();
return ret;
case MRT_DONE:
- if (sk!=mroute_socket)
+ if (sk != mroute_socket)
return -EACCES;
return ip_ra_control(sk, 0, NULL);
case MRT_ADD_VIF:
case MRT_DEL_VIF:
- if (optlen!=sizeof(vif))
+ if (optlen != sizeof(vif))
return -EINVAL;
- if (copy_from_user(&vif,optval,sizeof(vif)))
+ if (copy_from_user(&vif, optval, sizeof(vif)))
return -EFAULT;
if (vif.vifc_vifi >= MAXVIFS)
return -ENFILE;
rtnl_lock();
- if (optname==MRT_ADD_VIF) {
+ if (optname == MRT_ADD_VIF) {
ret = vif_add(&vif, sk==mroute_socket);
} else {
- ret = vif_delete(vif.vifc_vifi);
+ ret = vif_delete(vif.vifc_vifi, 0);
}
rtnl_unlock();
return ret;
*/
case MRT_ADD_MFC:
case MRT_DEL_MFC:
- if (optlen!=sizeof(mfc))
+ if (optlen != sizeof(mfc))
return -EINVAL;
- if (copy_from_user(&mfc,optval, sizeof(mfc)))
+ if (copy_from_user(&mfc, optval, sizeof(mfc)))
return -EFAULT;
rtnl_lock();
- if (optname==MRT_DEL_MFC)
+ if (optname == MRT_DEL_MFC)
ret = ipmr_mfc_delete(&mfc);
else
ret = ipmr_mfc_add(&mfc, sk==mroute_socket);
* Getsock opt support for the multicast routing system.
*/
-int ip_mroute_getsockopt(struct sock *sk,int optname,char __user *optval,int __user *optlen)
+int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int __user *optlen)
{
int olr;
int val;
- if (optname!=MRT_VERSION &&
+ if (optname != MRT_VERSION &&
#ifdef CONFIG_IP_PIMSM
optname!=MRT_PIM &&
#endif
if (olr < 0)
return -EINVAL;
- if (put_user(olr,optlen))
+ if (put_user(olr, optlen))
return -EFAULT;
- if (optname==MRT_VERSION)
- val=0x0305;
+ if (optname == MRT_VERSION)
+ val = 0x0305;
#ifdef CONFIG_IP_PIMSM
- else if (optname==MRT_PIM)
- val=mroute_do_pim;
+ else if (optname == MRT_PIM)
+ val = mroute_do_pim;
#endif
else
- val=mroute_do_assert;
- if (copy_to_user(optval,&val,olr))
+ val = mroute_do_assert;
+ if (copy_to_user(optval, &val, olr))
return -EFAULT;
return 0;
}
switch (cmd) {
case SIOCGETVIFCNT:
- if (copy_from_user(&vr,arg,sizeof(vr)))
+ if (copy_from_user(&vr, arg, sizeof(vr)))
return -EFAULT;
- if (vr.vifi>=maxvif)
+ if (vr.vifi >= maxvif)
return -EINVAL;
read_lock(&mrt_lock);
vif=&vif_table[vr.vifi];
if (VIF_EXISTS(vr.vifi)) {
- vr.icount=vif->pkt_in;
- vr.ocount=vif->pkt_out;
- vr.ibytes=vif->bytes_in;
- vr.obytes=vif->bytes_out;
+ vr.icount = vif->pkt_in;
+ vr.ocount = vif->pkt_out;
+ vr.ibytes = vif->bytes_in;
+ vr.obytes = vif->bytes_out;
read_unlock(&mrt_lock);
- if (copy_to_user(arg,&vr,sizeof(vr)))
+ if (copy_to_user(arg, &vr, sizeof(vr)))
return -EFAULT;
return 0;
}
read_unlock(&mrt_lock);
return -EADDRNOTAVAIL;
case SIOCGETSGCNT:
- if (copy_from_user(&sr,arg,sizeof(sr)))
+ if (copy_from_user(&sr, arg, sizeof(sr)))
return -EFAULT;
read_lock(&mrt_lock);
sr.wrong_if = c->mfc_un.res.wrong_if;
read_unlock(&mrt_lock);
- if (copy_to_user(arg,&sr,sizeof(sr)))
+ if (copy_to_user(arg, &sr, sizeof(sr)))
return -EFAULT;
return 0;
}
struct vif_device *v;
int ct;
- if (dev_net(dev) != &init_net)
+ if (!net_eq(dev_net(dev), &init_net))
return NOTIFY_DONE;
if (event != NETDEV_UNREGISTER)
return NOTIFY_DONE;
v=&vif_table[0];
- for (ct=0;ct<maxvif;ct++,v++) {
- if (v->dev==dev)
- vif_delete(ct);
+ for (ct=0; ct<maxvif; ct++,v++) {
+ if (v->dev == dev)
+ vif_delete(ct, 1);
}
return NOTIFY_DONE;
}
-static struct notifier_block ip_mr_notifier={
+static struct notifier_block ip_mr_notifier = {
.notifier_call = ipmr_device_event,
};
{
struct ip_options * opt = &(IPCB(skb)->opt);
- IP_INC_STATS_BH(IPSTATS_MIB_OUTFORWDATAGRAMS);
+ IP_INC_STATS_BH(dev_net(skb->dst->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);
if (unlikely(opt->optlen))
ip_forward_options(skb);
#ifdef CONFIG_IP_PIMSM
if (vif->flags & VIFF_REGISTER) {
vif->pkt_out++;
- vif->bytes_out+=skb->len;
+ vif->bytes_out += skb->len;
vif->dev->stats.tx_bytes += skb->len;
vif->dev->stats.tx_packets++;
ipmr_cache_report(skb, vifi, IGMPMSG_WHOLEPKT);
to blackhole.
*/
- IP_INC_STATS_BH(IPSTATS_MIB_FRAGFAILS);
+ IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
ip_rt_put(rt);
goto out_free;
}
}
vif->pkt_out++;
- vif->bytes_out+=skb->len;
+ vif->bytes_out += skb->len;
dst_release(skb->dst);
skb->dst = &rt->u.dst;
}
vif_table[vif].pkt_in++;
- vif_table[vif].bytes_in+=skb->len;
+ vif_table[vif].bytes_in += skb->len;
/*
* Forward the frame
if (skb2)
ipmr_queue_xmit(skb2, cache, psend);
}
- psend=ct;
+ psend = ct;
}
}
if (psend != -1) {
/*
* No usable cache entry
*/
- if (cache==NULL) {
+ if (cache == NULL) {
int vif;
if (local) {
if (dev)
RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
- mp_head = (struct rtattr*)skb_put(skb, RTA_LENGTH(0));
+ 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 = (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 = vif_table[ct].dev->ifindex;
read_lock(&mrt_lock);
cache = ipmr_cache_find(rt->rt_src, rt->rt_dst);
- if (cache==NULL) {
+ if (cache == NULL) {
struct sk_buff *skb2;
struct iphdr *iph;
struct net_device *dev;
* Setup for IP multicast routing
*/
-void __init ip_mr_init(void)
+int __init ip_mr_init(void)
{
+ int err;
+
mrt_cachep = kmem_cache_create("ip_mrt_cache",
sizeof(struct mfc_cache),
0, SLAB_HWCACHE_ALIGN|SLAB_PANIC,
NULL);
+ if (!mrt_cachep)
+ return -ENOMEM;
+
setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0);
- register_netdevice_notifier(&ip_mr_notifier);
+ err = register_netdevice_notifier(&ip_mr_notifier);
+ if (err)
+ goto reg_notif_fail;
#ifdef CONFIG_PROC_FS
- proc_net_fops_create(&init_net, "ip_mr_vif", 0, &ipmr_vif_fops);
- proc_net_fops_create(&init_net, "ip_mr_cache", 0, &ipmr_mfc_fops);
+ 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;
#endif
+ return 0;
+reg_notif_fail:
+ kmem_cache_destroy(mrt_cachep);
+#ifdef CONFIG_PROC_FS
+proc_vif_fail:
+ unregister_netdevice_notifier(&ip_mr_notifier);
+proc_cache_fail:
+ proc_net_remove(&init_net, "ip_mr_vif");
+#endif
+ return err;
}