Commit 63b5b609 authored by Stephen Hemminger's avatar Stephen Hemminger

[NET][IPMR] ipmr fixes

These patches fix ip multicast route (ipmr) on 2.5.73.

1 - Trivial C99 initialization
2 - Change functions/variables to static
3 - Drop and reacquire RTNL in error path
4 - Use time_after()
5 - Use alloc_netdev
6 - Fix OOPS on dropped packets
7 - Get rid of skb_linearize

Tested on 8-way SMP by bringing up pimd.
parent 3c79d64d
...@@ -217,7 +217,6 @@ struct pimreghdr ...@@ -217,7 +217,6 @@ struct pimreghdr
__u32 flags; __u32 flags;
}; };
extern int pim_rcv(struct sk_buff *);
extern int pim_rcv_v1(struct sk_buff *); extern int pim_rcv_v1(struct sk_buff *);
struct rtmsg; struct rtmsg;
......
...@@ -83,13 +83,13 @@ static int maxvif; ...@@ -83,13 +83,13 @@ static int maxvif;
#define VIF_EXISTS(idx) (vif_table[idx].dev != NULL) #define VIF_EXISTS(idx) (vif_table[idx].dev != NULL)
int mroute_do_assert; /* Set in PIM assert */ static int mroute_do_assert; /* Set in PIM assert */
int mroute_do_pim; static int mroute_do_pim;
static struct mfc_cache *mfc_cache_array[MFC_LINES]; /* Forwarding cache */ static struct mfc_cache *mfc_cache_array[MFC_LINES]; /* Forwarding cache */
static struct mfc_cache *mfc_unres_queue; /* Queue of unresolved entries */ static struct mfc_cache *mfc_unres_queue; /* Queue of unresolved entries */
atomic_t cache_resolve_queue_len; /* Size of unresolved */ static atomic_t cache_resolve_queue_len; /* Size of unresolved */
/* Special spinlock for queue of unresolved entries */ /* Special spinlock for queue of unresolved entries */
static spinlock_t mfc_unres_lock = SPIN_LOCK_UNLOCKED; static spinlock_t mfc_unres_lock = SPIN_LOCK_UNLOCKED;
...@@ -102,7 +102,7 @@ static spinlock_t mfc_unres_lock = SPIN_LOCK_UNLOCKED; ...@@ -102,7 +102,7 @@ static spinlock_t mfc_unres_lock = SPIN_LOCK_UNLOCKED;
In this case data path is free of exclusive locks at all. In this case data path is free of exclusive locks at all.
*/ */
kmem_cache_t *mrt_cachep; static kmem_cache_t *mrt_cachep;
static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local); static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local);
static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert); static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert);
...@@ -158,6 +158,10 @@ struct net_device *ipmr_new_tunnel(struct vifctl *v) ...@@ -158,6 +158,10 @@ struct net_device *ipmr_new_tunnel(struct vifctl *v)
return dev; return dev;
failure: failure:
/* allow the register to be completed before unregistering. */
rtnl_unlock();
rtnl_lock();
unregister_netdevice(dev); unregister_netdevice(dev);
return NULL; return NULL;
} }
...@@ -182,35 +186,23 @@ static struct net_device_stats *reg_vif_get_stats(struct net_device *dev) ...@@ -182,35 +186,23 @@ static struct net_device_stats *reg_vif_get_stats(struct net_device *dev)
return (struct net_device_stats*)dev->priv; return (struct net_device_stats*)dev->priv;
} }
static void vif_dev_destructor(struct net_device *dev) static void reg_vif_setup(struct net_device *dev)
{ {
kfree(dev); dev->type = ARPHRD_PIMREG;
dev->mtu = 1500 - sizeof(struct iphdr) - 8;
dev->flags = IFF_NOARP;
dev->hard_start_xmit = reg_vif_xmit;
dev->get_stats = reg_vif_get_stats;
dev->destructor = (void (*)(struct net_device *)) kfree;
} }
static static struct net_device *ipmr_reg_vif(void)
struct net_device *ipmr_reg_vif(struct vifctl *v)
{ {
struct net_device *dev; struct net_device *dev;
struct in_device *in_dev; struct in_device *in_dev;
int size;
size = sizeof(*dev) + sizeof(struct net_device_stats);
dev = kmalloc(size, GFP_KERNEL);
if (!dev)
return NULL;
memset(dev, 0, size);
dev->priv = dev + 1; dev = alloc_netdev(sizeof(struct net_device_stats), "pimreg",
reg_vif_setup);
strcpy(dev->name, "pimreg");
dev->type = ARPHRD_PIMREG;
dev->mtu = 1500 - sizeof(struct iphdr) - 8;
dev->flags = IFF_NOARP;
dev->hard_start_xmit = reg_vif_xmit;
dev->get_stats = reg_vif_get_stats;
dev->destructor = vif_dev_destructor;
if (register_netdevice(dev)) { if (register_netdevice(dev)) {
kfree(dev); kfree(dev);
...@@ -229,6 +221,10 @@ struct net_device *ipmr_reg_vif(struct vifctl *v) ...@@ -229,6 +221,10 @@ struct net_device *ipmr_reg_vif(struct vifctl *v)
return dev; return dev;
failure: failure:
/* allow the register to be completed before unregistering. */
rtnl_unlock();
rtnl_lock();
unregister_netdevice(dev); unregister_netdevice(dev);
return NULL; return NULL;
} }
...@@ -316,7 +312,7 @@ static void ipmr_destroy_unres(struct mfc_cache *c) ...@@ -316,7 +312,7 @@ static void ipmr_destroy_unres(struct mfc_cache *c)
/* Single timer process for all the unresolved queue. */ /* Single timer process for all the unresolved queue. */
void ipmr_expire_process(unsigned long dummy) static void ipmr_expire_process(unsigned long dummy)
{ {
unsigned long now; unsigned long now;
unsigned long expires; unsigned long expires;
...@@ -335,9 +331,8 @@ void ipmr_expire_process(unsigned long dummy) ...@@ -335,9 +331,8 @@ void ipmr_expire_process(unsigned long dummy)
cp = &mfc_unres_queue; cp = &mfc_unres_queue;
while ((c=*cp) != NULL) { while ((c=*cp) != NULL) {
long interval = c->mfc_un.unres.expires - now; if (time_after(c->mfc_un.unres.expires, now)) {
unsigned long interval = c->mfc_un.unres.expires - now;
if (interval > 0) {
if (interval < expires) if (interval < expires)
expires = interval; expires = interval;
cp = &c->next; cp = &c->next;
...@@ -397,7 +392,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock) ...@@ -397,7 +392,7 @@ static int vif_add(struct vifctl *vifc, int mrtsock)
*/ */
if (reg_vif_num >= 0) if (reg_vif_num >= 0)
return -EADDRINUSE; return -EADDRINUSE;
dev = ipmr_reg_vif(vifc); dev = ipmr_reg_vif();
if (!dev) if (!dev)
return -ENOBUFS; return -ENOBUFS;
break; break;
...@@ -683,7 +678,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb) ...@@ -683,7 +678,7 @@ ipmr_cache_unresolved(vifi_t vifi, struct sk_buff *skb)
* MFC cache manipulation by user space mroute daemon * MFC cache manipulation by user space mroute daemon
*/ */
int ipmr_mfc_delete(struct mfcctl *mfc) static int ipmr_mfc_delete(struct mfcctl *mfc)
{ {
int line; int line;
struct mfc_cache *c, **cp; struct mfc_cache *c, **cp;
...@@ -704,7 +699,7 @@ int ipmr_mfc_delete(struct mfcctl *mfc) ...@@ -704,7 +699,7 @@ int ipmr_mfc_delete(struct mfcctl *mfc)
return -ENOENT; return -ENOENT;
} }
int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock) static int ipmr_mfc_add(struct mfcctl *mfc, int mrtsock)
{ {
int line; int line;
struct mfc_cache *uc, *c, **cp; struct mfc_cache *uc, *c, **cp;
...@@ -1078,9 +1073,7 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v ...@@ -1078,9 +1073,7 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v
static struct notifier_block ip_mr_notifier={ static struct notifier_block ip_mr_notifier={
ipmr_device_event, .notifier_call = ipmr_device_event,
NULL,
0
}; };
/* /*
...@@ -1234,7 +1227,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c, ...@@ -1234,7 +1227,7 @@ static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c,
ipmr_forward_finish); ipmr_forward_finish);
} }
int ipmr_find_vif(struct net_device *dev) static int ipmr_find_vif(struct net_device *dev)
{ {
int ct; int ct;
for (ct=maxvif-1; ct>=0; ct--) { for (ct=maxvif-1; ct>=0; ct--) {
...@@ -1246,7 +1239,7 @@ int ipmr_find_vif(struct net_device *dev) ...@@ -1246,7 +1239,7 @@ int ipmr_find_vif(struct net_device *dev)
/* "local" means that we should preserve one skb (for local delivery) */ /* "local" means that we should preserve one skb (for local delivery) */
int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local) static int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local)
{ {
int psend = -1; int psend = -1;
int vif, ct; int vif, ct;
...@@ -1286,7 +1279,8 @@ int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local) ...@@ -1286,7 +1279,8 @@ int ip_mr_forward(struct sk_buff *skb, struct mfc_cache *cache, int local)
large chunk of pimd to kernel. Ough... --ANK large chunk of pimd to kernel. Ough... --ANK
*/ */
(mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) && (mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) &&
jiffies - cache->mfc_un.res.last_assert > MFC_ASSERT_THRESH) { time_after(jiffies,
cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
cache->mfc_un.res.last_assert = jiffies; cache->mfc_un.res.last_assert = jiffies;
ipmr_cache_report(skb, true_vifi, IGMPMSG_WRONGVIF); ipmr_cache_report(skb, true_vifi, IGMPMSG_WRONGVIF);
} }
...@@ -1406,24 +1400,19 @@ int ip_mr_input(struct sk_buff *skb) ...@@ -1406,24 +1400,19 @@ int ip_mr_input(struct sk_buff *skb)
int pim_rcv_v1(struct sk_buff * skb) int pim_rcv_v1(struct sk_buff * skb)
{ {
struct igmphdr *pim = (struct igmphdr*)skb->h.raw; struct igmphdr *pim;
struct iphdr *encap; struct iphdr *encap;
struct net_device *reg_dev = NULL; struct net_device *reg_dev = NULL;
if (skb_is_nonlinear(skb)) { if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
if (skb_linearize(skb, GFP_ATOMIC) != 0) { goto drop;
kfree_skb(skb);
return -ENOMEM;
}
pim = (struct igmphdr*)skb->h.raw; pim = (struct igmphdr*)skb->h.raw;
}
if (!mroute_do_pim || if (!mroute_do_pim ||
skb->len < sizeof(*pim) + sizeof(*encap) || skb->len < sizeof(*pim) + sizeof(*encap) ||
pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) { pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER)
kfree_skb(skb); goto drop;
return -EINVAL;
}
encap = (struct iphdr*)(skb->h.raw + sizeof(struct igmphdr)); encap = (struct iphdr*)(skb->h.raw + sizeof(struct igmphdr));
/* /*
...@@ -1433,11 +1422,9 @@ int pim_rcv_v1(struct sk_buff * skb) ...@@ -1433,11 +1422,9 @@ int pim_rcv_v1(struct sk_buff * skb)
c. packet is not truncated c. packet is not truncated
*/ */
if (!MULTICAST(encap->daddr) || if (!MULTICAST(encap->daddr) ||
ntohs(encap->tot_len) == 0 || encap->tot_len == 0 ||
ntohs(encap->tot_len) + sizeof(*pim) > skb->len) { ntohs(encap->tot_len) + sizeof(*pim) > skb->len)
kfree_skb(skb); goto drop;
return -EINVAL;
}
read_lock(&mrt_lock); read_lock(&mrt_lock);
if (reg_vif_num >= 0) if (reg_vif_num >= 0)
...@@ -1446,10 +1433,8 @@ int pim_rcv_v1(struct sk_buff * skb) ...@@ -1446,10 +1433,8 @@ int pim_rcv_v1(struct sk_buff * skb)
dev_hold(reg_dev); dev_hold(reg_dev);
read_unlock(&mrt_lock); read_unlock(&mrt_lock);
if (reg_dev == NULL) { if (reg_dev == NULL)
kfree_skb(skb); goto drop;
return -EINVAL;
}
skb->mac.raw = skb->nh.raw; skb->mac.raw = skb->nh.raw;
skb_pull(skb, (u8*)encap - skb->data); skb_pull(skb, (u8*)encap - skb->data);
...@@ -1470,41 +1455,35 @@ int pim_rcv_v1(struct sk_buff * skb) ...@@ -1470,41 +1455,35 @@ int pim_rcv_v1(struct sk_buff * skb)
netif_rx(skb); netif_rx(skb);
dev_put(reg_dev); dev_put(reg_dev);
return 0; return 0;
drop:
kfree_skb(skb);
return 0;
} }
#endif #endif
#ifdef CONFIG_IP_PIMSM_V2 #ifdef CONFIG_IP_PIMSM_V2
int pim_rcv(struct sk_buff * skb) static int pim_rcv(struct sk_buff * skb)
{ {
struct pimreghdr *pim = (struct pimreghdr*)skb->h.raw; struct pimreghdr *pim;
struct iphdr *encap; struct iphdr *encap;
struct net_device *reg_dev = NULL; struct net_device *reg_dev = NULL;
if (skb_is_nonlinear(skb)) { if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
if (skb_linearize(skb, GFP_ATOMIC) != 0) { goto drop;
kfree_skb(skb);
return -ENOMEM;
}
pim = (struct pimreghdr*)skb->h.raw;
}
if (skb->len < sizeof(*pim) + sizeof(*encap) || pim = (struct pimreghdr*)skb->h.raw;
pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) || if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
(pim->flags&PIM_NULL_REGISTER) || (pim->flags&PIM_NULL_REGISTER) ||
(ip_compute_csum((void *)pim, sizeof(*pim)) != 0 && (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
ip_compute_csum((void *)pim, skb->len))) { (u16)csum_fold(skb_checksum(skb, 0, skb->len, 0))))
kfree_skb(skb); goto drop;
return -EINVAL;
}
/* check if the inner packet is destined to mcast group */ /* check if the inner packet is destined to mcast group */
encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr)); encap = (struct iphdr*)(skb->h.raw + sizeof(struct pimreghdr));
if (!MULTICAST(encap->daddr) || if (!MULTICAST(encap->daddr) ||
ntohs(encap->tot_len) == 0 || encap->tot_len == 0 ||
ntohs(encap->tot_len) + sizeof(*pim) > skb->len) { ntohs(encap->tot_len) + sizeof(*pim) > skb->len)
kfree_skb(skb); goto drop;
return -EINVAL;
}
read_lock(&mrt_lock); read_lock(&mrt_lock);
if (reg_vif_num >= 0) if (reg_vif_num >= 0)
...@@ -1513,10 +1492,8 @@ int pim_rcv(struct sk_buff * skb) ...@@ -1513,10 +1492,8 @@ int pim_rcv(struct sk_buff * skb)
dev_hold(reg_dev); dev_hold(reg_dev);
read_unlock(&mrt_lock); read_unlock(&mrt_lock);
if (reg_dev == NULL) { if (reg_dev == NULL)
kfree_skb(skb); goto drop;
return -EINVAL;
}
skb->mac.raw = skb->nh.raw; skb->mac.raw = skb->nh.raw;
skb_pull(skb, (u8*)encap - skb->data); skb_pull(skb, (u8*)encap - skb->data);
...@@ -1537,6 +1514,9 @@ int pim_rcv(struct sk_buff * skb) ...@@ -1537,6 +1514,9 @@ int pim_rcv(struct sk_buff * skb)
netif_rx(skb); netif_rx(skb);
dev_put(reg_dev); dev_put(reg_dev);
return 0; return 0;
drop:
kfree_skb(skb);
return 0;
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment