/*
* 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
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
- * Version: $Id: ipmr.c,v 1.65 2001/10/31 21:55:54 davem Exp $
- *
* Fixes:
* Michael Chastain : Incorrect size of copying.
* Alan Cox : Added the cache manager code
#include <linux/mroute.h>
#include <linux/init.h>
#include <linux/if_ether.h>
+#include <net/net_namespace.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
#include <linux/netfilter_ipv4.h>
#include <net/ipip.h>
#include <net/checksum.h>
+#include <net/netlink.h>
#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
#define CONFIG_IP_PIMSM 1
/* 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)
{
struct net_device *dev;
- dev = __dev_get_by_name("tunl0");
+ 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;
p.iph.ihl = 5;
p.iph.protocol = IPPROTO_IPIP;
sprintf(p.name, "dvmrp%d", v->vifc_vifi);
- ifr.ifr_ifru.ifru_data = (void*)&p;
+ 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 (err == 0 && (dev = __dev_get_by_name(p.name)) != NULL) {
+ if (err == 0 && (dev = __dev_get_by_name(&init_net, p.name)) != NULL) {
dev->flags |= IFF_MULTICAST;
in_dev = __in_dev_get_rtnl(dev);
- if (in_dev == NULL && (in_dev = inetdev_init(dev)) == NULL)
+ if (in_dev == NULL)
goto failure;
- in_dev->cnf.rp_filter = 0;
+
+ ipv4_devconf_setall(in_dev);
+ IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
if (dev_open(dev))
goto failure;
+ dev_hold(dev);
}
}
return dev;
static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
{
read_lock(&mrt_lock);
- ((struct net_device_stats*)netdev_priv(dev))->tx_bytes += skb->len;
- ((struct net_device_stats*)netdev_priv(dev))->tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ dev->stats.tx_packets++;
ipmr_cache_report(skb, reg_vif_num, IGMPMSG_WHOLEPKT);
read_unlock(&mrt_lock);
kfree_skb(skb);
return 0;
}
-static struct net_device_stats *reg_vif_get_stats(struct net_device *dev)
-{
- return (struct net_device_stats*)netdev_priv(dev);
-}
+static const struct net_device_ops reg_vif_netdev_ops = {
+ .ndo_start_xmit = reg_vif_xmit,
+};
static void reg_vif_setup(struct net_device *dev)
{
dev->type = ARPHRD_PIMREG;
dev->mtu = ETH_DATA_LEN - sizeof(struct iphdr) - 8;
dev->flags = IFF_NOARP;
- dev->hard_start_xmit = reg_vif_xmit;
- dev->get_stats = reg_vif_get_stats;
+ dev->netdev_ops = ®_vif_netdev_ops,
dev->destructor = free_netdev;
}
struct net_device *dev;
struct in_device *in_dev;
- dev = alloc_netdev(sizeof(struct net_device_stats), "pimreg",
- reg_vif_setup);
+ dev = alloc_netdev(0, "pimreg", reg_vif_setup);
if (dev == NULL)
return NULL;
}
dev->iflink = 0;
- if ((in_dev = inetdev_init(dev)) == NULL)
+ rcu_read_lock();
+ if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {
+ rcu_read_unlock();
goto failure;
+ }
- in_dev->cnf.rp_filter = 0;
+ ipv4_devconf_setall(in_dev);
+ IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
+ rcu_read_unlock();
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;
dev_set_allmulti(dev, -1);
if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) {
- in_dev->cnf.mc_forwarding--;
+ IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
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))) {
- if (skb->nh.iph->version == 0) {
+ 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;
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
e->error = -ETIMEDOUT;
memset(&e->msg, 0, sizeof(e->msg));
- rtnl_unicast(skb, NETLINK_CB(skb).pid);
+ rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid);
} else
kfree_skb(skb);
}
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(vifc->vifc_lcl_addr.s_addr);
+ 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;
- in_dev->cnf.mc_forwarding++;
- dev_set_allmulti(dev, +1);
+ IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
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))) {
- if (skb->nh.iph->version == 0) {
+ 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));
if (ipmr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) {
- nlh->nlmsg_len = skb->tail - (u8*)nlh;
+ nlh->nlmsg_len = (skb_tail_pointer(skb) -
+ (u8 *)nlh);
} else {
nlh->nlmsg_type = NLMSG_ERROR;
nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
memset(&e->msg, 0, sizeof(e->msg));
}
- rtnl_unicast(skb, NETLINK_CB(skb).pid);
+ rtnl_unicast(skb, &init_net, NETLINK_CB(skb).pid);
} else
ip_mr_forward(skb, c, 0);
}
static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
{
struct sk_buff *skb;
- int ihl = pkt->nh.iph->ihl<<2;
+ const int ihl = ip_hdrlen(pkt);
struct igmphdr *igmp;
struct igmpmsg *msg;
int ret;
And all this only to mangle msg->im_msgtype and
to set msg->im_mbz to "mbz" :-)
*/
- msg = (struct igmpmsg*)skb_push(skb, sizeof(struct iphdr));
- skb->nh.raw = skb->h.raw = (u8*)msg;
- memcpy(msg, pkt->nh.raw, sizeof(struct iphdr));
+ skb_push(skb, sizeof(struct iphdr));
+ skb_reset_network_header(skb);
+ skb_reset_transport_header(skb);
+ msg = (struct igmpmsg *)skb_network_header(skb);
+ memcpy(msg, skb_network_header(pkt), sizeof(struct iphdr));
msg->im_msgtype = IGMPMSG_WHOLEPKT;
msg->im_mbz = 0;
msg->im_vif = reg_vif_num;
- skb->nh.iph->ihl = sizeof(struct iphdr) >> 2;
- skb->nh.iph->tot_len = htons(ntohs(pkt->nh.iph->tot_len) + sizeof(struct iphdr));
+ ip_hdr(skb)->ihl = sizeof(struct iphdr) >> 2;
+ ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(pkt)->tot_len) +
+ sizeof(struct iphdr));
} else
#endif
{
* Copy the IP header
*/
- skb->nh.iph = (struct iphdr *)skb_put(skb, ihl);
- memcpy(skb->data,pkt->data,ihl);
- skb->nh.iph->protocol = 0; /* Flag to the kernel this is a route add */
- msg = (struct igmpmsg*)skb->nh.iph;
+ skb->network_header = skb->tail;
+ skb_put(skb, ihl);
+ skb_copy_to_linear_data(skb, pkt->data, ihl);
+ ip_hdr(skb)->protocol = 0; /* Flag to the kernel this is a route add */
+ msg = (struct igmpmsg *)skb_network_header(skb);
msg->im_vif = vifi;
skb->dst = dst_clone(pkt->dst);
* 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;
- skb->nh.iph->tot_len=htons(skb->len); /* Fix the length */
- skb->h.raw = skb->nh.raw;
+ ip_hdr(skb)->tot_len = htons(skb->len); /* Fix the length */
+ skb->transport_header = skb->network_header;
}
if (mroute_socket == NULL) {
/*
* 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);
{
int err;
struct mfc_cache *c;
+ const struct iphdr *iph = ip_hdr(skb);
spin_lock_bh(&mfc_unres_lock);
for (c=mfc_unres_queue; c; c=c->next) {
- if (c->mfc_mcastgrp == skb->nh.iph->daddr &&
- c->mfc_origin == skb->nh.iph->saddr)
+ if (c->mfc_mcastgrp == iph->daddr &&
+ c->mfc_origin == iph->saddr)
break;
}
* 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);
/*
* Fill in the new cache entry
*/
- c->mfc_parent=-1;
- c->mfc_origin=skb->nh.iph->saddr;
- c->mfc_mcastgrp=skb->nh.iph->daddr;
+ c->mfc_parent = -1;
+ c->mfc_origin = iph->saddr;
+ c->mfc_mcastgrp = iph->daddr;
/*
* Reflect first query at mrouted.
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 &&
return 0;
}
- if (!MULTICAST(mfc->mfcc_mcastgrp.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];
{
rtnl_lock();
if (sk == mroute_socket) {
- ipv4_devconf.mc_forwarding--;
+ 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.mc_forwarding++;
+ 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);
#ifdef CONFIG_IP_PIMSM
case MRT_PIM:
{
- int v, ret;
+ int v;
+
if (get_user(v,(int __user *)optval))
return -EFAULT;
- v = (v)?1:0;
+ v = (v) ? 1 : 0;
+
rtnl_lock();
ret = 0;
if (v != mroute_do_pim) {
* 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;
}
static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
+ struct net_device *dev = ptr;
struct vif_device *v;
int ct;
+
+ 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==ptr)
- 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,
};
static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
{
- struct iphdr *iph = (struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+ struct iphdr *iph;
+ struct iphdr *old_iph = ip_hdr(skb);
+
+ skb_push(skb, sizeof(struct iphdr));
+ skb->transport_header = skb->network_header;
+ skb_reset_network_header(skb);
+ iph = ip_hdr(skb);
iph->version = 4;
- iph->tos = skb->nh.iph->tos;
- iph->ttl = skb->nh.iph->ttl;
+ iph->tos = old_iph->tos;
+ iph->ttl = old_iph->ttl;
iph->frag_off = 0;
iph->daddr = daddr;
iph->saddr = saddr;
ip_select_ident(iph, skb->dst, NULL);
ip_send_check(iph);
- skb->h.ipiph = skb->nh.iph;
- skb->nh.iph = iph;
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
nf_reset(skb);
}
{
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);
static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, int vifi)
{
- struct iphdr *iph = skb->nh.iph;
+ const struct iphdr *iph = ip_hdr(skb);
struct vif_device *vif = &vif_table[vifi];
struct net_device *dev;
struct rtable *rt;
#ifdef CONFIG_IP_PIMSM
if (vif->flags & VIFF_REGISTER) {
vif->pkt_out++;
- vif->bytes_out+=skb->len;
- ((struct net_device_stats*)netdev_priv(vif->dev))->tx_bytes += skb->len;
- ((struct net_device_stats*)netdev_priv(vif->dev))->tx_packets++;
+ vif->bytes_out += skb->len;
+ vif->dev->stats.tx_bytes += skb->len;
+ vif->dev->stats.tx_packets++;
ipmr_cache_report(skb, vifi, IGMPMSG_WHOLEPKT);
kfree_skb(skb);
return;
.saddr = vif->local,
.tos = RT_TOS(iph->tos) } },
.proto = IPPROTO_IPIP };
- if (ip_route_output_key(&rt, &fl))
+ if (ip_route_output_key(&init_net, &rt, &fl))
goto out_free;
encap = sizeof(struct iphdr);
} else {
{ .daddr = iph->daddr,
.tos = RT_TOS(iph->tos) } },
.proto = IPPROTO_IPIP };
- if (ip_route_output_key(&rt, &fl))
+ if (ip_route_output_key(&init_net, &rt, &fl))
goto out_free;
}
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;
- iph = skb->nh.iph;
- ip_decrease_ttl(iph);
+ 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 */
- ((struct ip_tunnel *)netdev_priv(vif->dev))->stat.tx_packets++;
- ((struct ip_tunnel *)netdev_priv(vif->dev))->stat.tx_bytes+=skb->len;
+ vif->dev->stats.tx_packets++;
+ vif->dev->stats.tx_bytes += skb->len;
}
IPCB(skb)->flags |= IPSKB_FORWARDED;
* not mrouter) cannot join to more than one interface - it will
* result in receiving multiple packets.
*/
- NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, dev,
+ NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, dev,
ipmr_forward_finish);
return;
if (vif_table[vif].dev != skb->dev) {
int true_vifi;
- if (((struct rtable*)skb->dst)->fl.iif == 0) {
+ if (skb->rtable->fl.iif == 0) {
/* It is our own packet, looped back.
Very complicated situation...
}
vif_table[vif].pkt_in++;
- vif_table[vif].bytes_in+=skb->len;
+ 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 (skb->nh.iph->ttl > cache->mfc_un.res.ttls[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);
}
- psend=ct;
+ psend = ct;
}
}
if (psend != -1) {
int ip_mr_input(struct sk_buff *skb)
{
struct mfc_cache *cache;
- int local = ((struct rtable*)skb->dst)->rt_flags&RTCF_LOCAL;
+ 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)->opt.router_alert) {
if (ip_call_ra_chain(skb))
return 0;
- } else if (skb->nh.iph->protocol == IPPROTO_IGMP){
+ } 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
}
read_lock(&mrt_lock);
- cache = ipmr_cache_find(skb->nh.iph->saddr, skb->nh.iph->daddr);
+ cache = ipmr_cache_find(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
/*
* No usable cache entry
*/
- if (cache==NULL) {
+ if (cache == NULL) {
int vif;
if (local) {
return 0;
}
-#ifdef CONFIG_IP_PIMSM_V1
-/*
- * Handle IGMP messages of PIMv1
- */
-
-int pim_rcv_v1(struct sk_buff * skb)
+#ifdef CONFIG_IP_PIMSM
+static int __pim_rcv(struct sk_buff *skb, unsigned int pimlen)
{
- struct igmphdr *pim;
- struct iphdr *encap;
- struct net_device *reg_dev = NULL;
-
- if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
- goto drop;
-
- pim = (struct igmphdr*)skb->h.raw;
-
- if (!mroute_do_pim ||
- skb->len < sizeof(*pim) + sizeof(*encap) ||
- pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
- goto drop;
+ struct net_device *reg_dev = NULL;
+ struct iphdr *encap;
- encap = (struct iphdr*)(skb->h.raw + sizeof(struct igmphdr));
+ 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 (!MULTICAST(encap->daddr) ||
+ if (!ipv4_is_multicast(encap->daddr) ||
encap->tot_len == 0 ||
- ntohs(encap->tot_len) + sizeof(*pim) > skb->len)
- goto drop;
+ ntohs(encap->tot_len) + pimlen > skb->len)
+ return 1;
read_lock(&mrt_lock);
if (reg_vif_num >= 0)
read_unlock(&mrt_lock);
if (reg_dev == NULL)
- goto drop;
+ return 1;
- skb->mac.raw = skb->nh.raw;
+ skb->mac_header = skb->network_header;
skb_pull(skb, (u8*)encap - skb->data);
- skb->nh.iph = (struct iphdr *)skb->data;
+ 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;
- ((struct net_device_stats*)netdev_priv(reg_dev))->rx_bytes += skb->len;
- ((struct net_device_stats*)netdev_priv(reg_dev))->rx_packets++;
+ reg_dev->stats.rx_bytes += skb->len;
+ reg_dev->stats.rx_packets++;
nf_reset(skb);
netif_rx(skb);
dev_put(reg_dev);
+
return 0;
- drop:
- kfree_skb(skb);
+}
+#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 (!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
static int pim_rcv(struct sk_buff * skb)
{
struct pimreghdr *pim;
- struct iphdr *encap;
- struct net_device *reg_dev = NULL;
- if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
+ if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(struct iphdr)))
goto drop;
- pim = (struct pimreghdr*)skb->h.raw;
+ pim = (struct pimreghdr *)skb_transport_header(skb);
if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
(pim->flags&PIM_NULL_REGISTER) ||
(ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
csum_fold(skb_checksum(skb, 0, skb->len, 0))))
goto drop;
- /* check if the inner packet is destined to mcast group */
- encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr));
- if (!MULTICAST(encap->daddr) ||
- encap->tot_len == 0 ||
- ntohs(encap->tot_len) + sizeof(*pim) > skb->len)
- goto drop;
-
- read_lock(&mrt_lock);
- if (reg_vif_num >= 0)
- reg_dev = vif_table[reg_vif_num].dev;
- if (reg_dev)
- dev_hold(reg_dev);
- read_unlock(&mrt_lock);
-
- if (reg_dev == NULL)
- goto drop;
-
- skb->mac.raw = skb->nh.raw;
- skb_pull(skb, (u8*)encap - skb->data);
- skb->nh.iph = (struct iphdr *)skb->data;
- skb->dev = reg_dev;
- skb->protocol = htons(ETH_P_IP);
- skb->ip_summed = 0;
- skb->pkt_type = PACKET_HOST;
- dst_release(skb->dst);
- ((struct net_device_stats*)netdev_priv(reg_dev))->rx_bytes += skb->len;
- ((struct net_device_stats*)netdev_priv(reg_dev))->rx_packets++;
- skb->dst = NULL;
- nf_reset(skb);
- netif_rx(skb);
- dev_put(reg_dev);
- return 0;
- drop:
- kfree_skb(skb);
+ if (__pim_rcv(skb, sizeof(*pim))) {
+drop:
+ kfree_skb(skb);
+ }
return 0;
}
#endif
int ct;
struct rtnexthop *nhp;
struct net_device *dev = vif_table[c->mfc_parent].dev;
- u8 *b = skb->tail;
+ 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));
+ 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;
}
}
mp_head->rta_type = RTA_MULTIPATH;
- mp_head->rta_len = skb->tail - (u8*)mp_head;
+ mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
rtm->rtm_type = RTN_MULTICAST;
return 1;
rtattr_failure:
- skb_trim(skb, b - skb->data);
+ nlmsg_trim(skb, b);
return -EMSGSIZE;
}
{
int err;
struct mfc_cache *cache;
- struct rtable *rt = (struct rtable*)skb->dst;
+ struct rtable *rt = skb->rtable;
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;
int vif;
skb_push(skb2, sizeof(struct iphdr));
skb_reset_network_header(skb2);
- skb2->nh.iph->ihl = sizeof(struct iphdr)>>2;
- skb2->nh.iph->saddr = rt->rt_src;
- skb2->nh.iph->daddr = rt->rt_dst;
- skb2->nh.iph->version = 0;
+ 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;
}
static void *ipmr_vif_seq_start(struct seq_file *seq, loff_t *pos)
+ __acquires(mrt_lock)
{
read_lock(&mrt_lock);
return *pos ? ipmr_vif_seq_idx(seq->private, *pos - 1)
}
static void ipmr_vif_seq_stop(struct seq_file *seq, void *v)
+ __releases(mrt_lock)
{
read_unlock(&mrt_lock);
}
return 0;
}
-static struct seq_operations ipmr_vif_seq_ops = {
+static const struct seq_operations ipmr_vif_seq_ops = {
.start = ipmr_vif_seq_start,
.next = ipmr_vif_seq_next,
.stop = ipmr_vif_seq_stop,
static int ipmr_vif_open(struct inode *inode, struct file *file)
{
- struct seq_file *seq;
- int rc = -ENOMEM;
- struct ipmr_vif_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);
-
- if (!s)
- goto out;
-
- rc = seq_open(file, &ipmr_vif_seq_ops);
- if (rc)
- goto out_kfree;
-
- s->ct = 0;
- seq = file->private_data;
- seq->private = s;
-out:
- return rc;
-out_kfree:
- kfree(s);
- goto out;
-
+ return seq_open_private(file, &ipmr_vif_seq_ops,
+ sizeof(struct ipmr_vif_iter));
}
static const struct file_operations ipmr_vif_fops = {
const struct mfc_cache *mfc = v;
const struct ipmr_mfc_iter *it = seq->private;
- seq_printf(seq, "%08lX %08lX %-3d %8ld %8ld %8ld",
+ seq_printf(seq, "%08lX %08lX %-3hd",
(unsigned long) mfc->mfc_mcastgrp,
(unsigned long) mfc->mfc_origin,
- mfc->mfc_parent,
- mfc->mfc_un.res.pkt,
- mfc->mfc_un.res.bytes,
- mfc->mfc_un.res.wrong_if);
+ mfc->mfc_parent);
if (it->cache != &mfc_unres_queue) {
+ 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(n)
" %2d:%-3d",
n, mfc->mfc_un.res.ttls[n]);
}
+ } 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 struct seq_operations ipmr_mfc_seq_ops = {
+static const struct seq_operations ipmr_mfc_seq_ops = {
.start = ipmr_mfc_seq_start,
.next = ipmr_mfc_seq_next,
.stop = ipmr_mfc_seq_stop,
static int ipmr_mfc_open(struct inode *inode, struct file *file)
{
- struct seq_file *seq;
- int rc = -ENOMEM;
- struct ipmr_mfc_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);
-
- if (!s)
- goto out;
-
- rc = seq_open(file, &ipmr_mfc_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_private(file, &ipmr_mfc_seq_ops,
+ sizeof(struct ipmr_mfc_iter));
}
static const struct file_operations ipmr_mfc_fops = {
* 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, NULL);
- init_timer(&ipmr_expire_timer);
- ipmr_expire_timer.function=ipmr_expire_process;
- register_netdevice_notifier(&ip_mr_notifier);
+ NULL);
+ if (!mrt_cachep)
+ return -ENOMEM;
+
+ setup_timer(&ipmr_expire_timer, ipmr_expire_process, 0);
+ err = register_netdevice_notifier(&ip_mr_notifier);
+ if (err)
+ goto reg_notif_fail;
#ifdef CONFIG_PROC_FS
- proc_net_fops_create("ip_mr_vif", 0, &ipmr_vif_fops);
- proc_net_fops_create("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;
+#ifdef CONFIG_PROC_FS
+proc_cache_fail:
+ proc_net_remove(&init_net, "ip_mr_vif");
+proc_vif_fail:
+ unregister_netdevice_notifier(&ip_mr_notifier);
+#endif
+reg_notif_fail:
+ del_timer(&ipmr_expire_timer);
+ kmem_cache_destroy(mrt_cachep);
+ return err;
}