summaryrefslogtreecommitdiffstats
path: root/net/decnet/dn_route.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /net/decnet/dn_route.c
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'net/decnet/dn_route.c')
-rw-r--r--net/decnet/dn_route.c550
1 files changed, 260 insertions, 290 deletions
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 06001b5f5..c0ca04ac0 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -16,6 +16,12 @@
* Steve Whitehouse : Timeouts for cached routes.
* Steve Whitehouse : Use dst cache for input routes too.
* Steve Whitehouse : Fixed error values in dn_send_skb.
+ * Steve Whitehouse : Rework routing functions to better fit
+ * DECnet routing design
+ * Alexey Kuznetsov : New SMP locking
+ * Steve Whitehouse : More SMP locking changes & dn_cache_dump()
+ * Steve Whitehouse : Prerouting NF hook, now really is prerouting.
+ * Fixed possible skb leak in rtnetlink funcs.
*/
/******************************************************************************
@@ -49,9 +55,9 @@
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
-#include <linux/firewall.h>
#include <linux/rtnetlink.h>
#include <linux/string.h>
+#include <linux/netfilter_decnet.h>
#include <asm/errno.h>
#include <net/neighbour.h>
#include <net/dst.h>
@@ -77,6 +83,8 @@ static void dn_dst_link_failure(struct sk_buff *);
static int dn_route_input(struct sk_buff *);
static struct dn_route *dn_route_cache[DN_HASHBUCKETS];
+static rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
+
static struct timer_list dn_route_timer = { NULL, NULL, 0, 0L, NULL };
int decnet_dst_gc_interval = 2;
@@ -90,6 +98,7 @@ static struct dst_ops dn_dst_ops = {
NULL,
dn_dst_negative_advice,
dn_dst_link_failure,
+ sizeof(struct dn_route),
ATOMIC_INIT(0)
};
@@ -108,14 +117,17 @@ static void dn_dst_check_expire(unsigned long dummy)
for(i = 0; i < DN_HASHBUCKETS; i++) {
rtp = &dn_route_cache[i];
+
+ write_lock(&dn_hash_lock);
for(;(rt=*rtp); rtp = &rt->u.rt_next) {
- if (atomic_read(&rt->u.dst.use) ||
+ if (atomic_read(&rt->u.dst.__refcnt) ||
(now - rt->u.dst.lastuse) < expire)
continue;
*rtp = rt->u.rt_next;
rt->u.rt_next = NULL;
dst_free(&rt->u.dst);
}
+ write_unlock(&dn_hash_lock);
if ((jiffies - now) > 0)
break;
@@ -132,11 +144,11 @@ static int dn_dst_gc(void)
unsigned long now = jiffies;
unsigned long expire = 10 * HZ;
- start_bh_atomic();
+ write_lock_bh(&dn_hash_lock);
for(i = 0; i < DN_HASHBUCKETS; i++) {
rtp = &dn_route_cache[i];
for(; (rt=*rtp); rtp = &rt->u.rt_next) {
- if (atomic_read(&rt->u.dst.use) ||
+ if (atomic_read(&rt->u.dst.__refcnt) ||
(now - rt->u.dst.lastuse) < expire)
continue;
*rtp = rt->u.rt_next;
@@ -145,7 +157,7 @@ static int dn_dst_gc(void)
break;
}
}
- end_bh_atomic();
+ write_unlock_bh(&dn_hash_lock);
return 0;
}
@@ -182,24 +194,23 @@ static void dn_insert_route(struct dn_route *rt)
unsigned hash = dn_hash(rt->rt_daddr);
unsigned long now = jiffies;
- start_bh_atomic();
-
+ write_lock_bh(&dn_hash_lock);
rt->u.rt_next = dn_route_cache[hash];
dn_route_cache[hash] = rt;
- atomic_inc(&rt->u.dst.refcnt);
- atomic_inc(&rt->u.dst.use);
+ dst_hold(&rt->u.dst);
+ rt->u.dst.__use++;
rt->u.dst.lastuse = now;
- end_bh_atomic();
+ write_unlock_bh(&dn_hash_lock);
}
-#if defined(CONFIG_DECNET_MODULE)
-static void dn_run_flush(unsigned long dummy)
+void dn_run_flush(unsigned long dummy)
{
int i;
struct dn_route *rt, *next;
+ write_lock_bh(&dn_hash_lock);
for(i = 0; i < DN_HASHBUCKETS; i++) {
if ((rt = xchg(&dn_route_cache[i], NULL)) == NULL)
continue;
@@ -210,8 +221,19 @@ static void dn_run_flush(unsigned long dummy)
dst_free((struct dst_entry *)rt);
}
}
+ write_unlock_bh(&dn_hash_lock);
+}
+
+static int dn_route_rx_packet(struct sk_buff *skb)
+{
+ int err;
+
+ if ((err = dn_route_input(skb)) == 0)
+ return skb->dst->input(skb);
+
+ kfree_skb(skb);
+ return err;
}
-#endif /* CONFIG_DECNET_MODULE */
static int dn_route_rx_long(struct sk_buff *skb)
{
@@ -242,24 +264,7 @@ static int dn_route_rx_long(struct sk_buff *skb)
ptr++;
cb->hops = *ptr++; /* Visit Count */
- if (dn_route_input(skb) == 0) {
-
-#ifdef CONFIG_DECNET_FW
- struct neighbour *neigh = skb->dst->neighbour;
-
- switch(call_in_firewall(PF_DECnet, skb->dev, NULL, NULL, &skb)) {
- case FW_REJECT:
- neigh->ops->error_report(neigh, skb);
- return 0;
- case FW_BLOCK:
- default:
- goto drop_it;
- case FW_ACCEPT:
- }
-#endif /* CONFIG_DECNET_FW */
-
- return skb->dst->input(skb);
- }
+ return NF_HOOK(PF_DECnet, NF_DN_PRE_ROUTING, skb, skb->dev, NULL, dn_route_rx_packet);
drop_it:
kfree_skb(skb);
@@ -285,32 +290,14 @@ static int dn_route_rx_short(struct sk_buff *skb)
ptr += 2;
cb->hops = *ptr & 0x3f;
- if (dn_route_input(skb) == 0) {
-
-#ifdef CONFIG_DECNET_FW
- struct neighbour *neigh = skb->dst->neighbour;
-
- switch(call_in_firewall(PF_DECnet, skb->dev, NULL, NULL, &skb)) {
- case FW_REJECT:
- neigh->ops->error_report(neigh, skb);
- return 0;
- case FW_BLOCK:
- default:
- goto drop_it;
-
- case FW_ACCEPT:
- }
-#endif /* CONFIG_DECNET_FW */
-
- return skb->dst->input(skb);
- }
+ return NF_HOOK(PF_DECnet, NF_DN_PRE_ROUTING, skb, skb->dev, NULL, dn_route_rx_packet);
drop_it:
kfree_skb(skb);
return 0;
}
-int dn_route_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
unsigned char flags = 0;
@@ -352,11 +339,11 @@ int dn_route_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt
cb->rt_flags = flags;
- if (dn->parms.setsrc)
- dn->parms.setsrc(skb);
-
- /* printk(KERN_DEBUG "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n", (int)flags,
- (dev) ? dev->name : "???", len, skb->len, padlen); */
+ if (decnet_debug_level & 1)
+ printk(KERN_DEBUG
+ "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n",
+ (int)flags, (dev) ? dev->name : "???", len, skb->len,
+ padlen);
#ifdef CONFIG_DECNET_RAW
dn_raw_rx_routing(skb);
@@ -411,59 +398,21 @@ int dn_route_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt
}
dump_it:
- if (net_ratelimit())
- printk(KERN_DEBUG "dn_route_rcv: Dumping packet\n");
kfree_skb(skb);
return 0;
}
-
-void dn_send_skb(struct sk_buff *skb)
-{
- struct sock *sk = skb->sk;
- struct dn_scp *scp = &sk->protinfo.dn;
-
- if (sk == NULL) {
- dev_queue_xmit(skb);
- return ;
- }
-
- skb->h.raw = skb->data;
-
- scp->stamp = jiffies; /* Record time packet was sent */
-
- /* printk(KERN_DEBUG "dn_send_skb\n"); */
-
- if (sk->dst_cache && sk->dst_cache->obsolete) {
- dst_release(sk->dst_cache);
- sk->dst_cache = NULL;
- }
-
- if (sk->dst_cache == NULL) {
- if (dn_route_output(sk) != 0) {
- kfree_skb(skb);
- sk->err = EHOSTUNREACH;
- if (!sk->dead)
- sk->state_change(sk);
- return;
- }
- }
-
- skb->dst = dst_clone(sk->dst_cache);
-
- sk->dst_cache->output(skb);
-}
-
-
static int dn_output(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct dn_route *rt = (struct dn_route *)dst;
- struct device *dev = dst->dev;
+ struct net_device *dev = dst->dev;
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct neighbour *neigh;
+
int err = -EINVAL;
- if (!dst->neighbour)
+ if ((neigh = dst->neighbour) == NULL)
goto error;
skb->dev = dev;
@@ -480,44 +429,26 @@ static int dn_output(struct sk_buff *skb)
cb->rt_flags |= DN_RT_F_IE;
cb->hops = 0;
- /*
- * Filter through the outgoing firewall
- */
-#ifdef CONFIG_DECNET_FW
- switch(call_out_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
- case FW_REJECT:
- err = -EPERM;
- goto drop;
- case FW_BLOCK:
- default:
- err = 0;
- goto drop;
- case FW_ACCEPT:
- }
-#endif /* CONFIG_DECNET_FW */
-
- return dst->neighbour->output(skb);
+ return NF_HOOK(PF_DECnet, NF_DN_LOCAL_OUT, skb, NULL, dev, neigh->output);
error:
if (net_ratelimit())
printk(KERN_DEBUG "dn_output: This should not happen\n");
-#ifdef CONFIG_DECNET_FW
-drop:
-#endif
kfree_skb(skb);
return err;
}
#ifdef CONFIG_DECNET_ROUTER
-static int dn_l2_forward(struct sk_buff *skb)
+static int dn_forward(struct sk_buff *skb)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
struct dst_entry *dst = skb->dst;
+ struct neighbour *neigh;
int err = -EINVAL;
- if (!dst->neighbour)
+ if ((neigh = dst->neighbour) == NULL)
goto error;
/*
@@ -527,21 +458,6 @@ static int dn_l2_forward(struct sk_buff *skb)
if (++cb->hops > 30)
goto drop;
- /*
- * Forwarding firewall
- */
-#ifdef CONFIG_DECNET_FW
- switch(call_fw_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
- case FW_REJECT:
- dst->neighbour->ops->error_report(dst->neighbour, skb);
- return -EPERM;
- case FW_BLOCK:
- default:
- goto drop;
- case FW_ACCEPT:
- }
-#endif /* CONFIG_DECNET_FW */
-
skb->dev = dst->dev;
/*
@@ -554,19 +470,7 @@ static int dn_l2_forward(struct sk_buff *skb)
else
cb->rt_flags &= ~DN_RT_F_IE;
-#ifdef CONFIG_DECNET_FW
- switch(call_out_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
- case FW_REJECT:
- dst->neighbour->ops->error_report(dst->neighbour, skb);
- return -EPERM;
- case FW_BLOCK:
- default:
- goto drop;
- case FW_ACCEPT:
- }
-#endif /* CONFIG_DECNET_FW */
-
- return dst->neighbour->output(skb);
+ return NF_HOOK(PF_DECnet, NF_DN_FORWARD, skb, skb->rx_dev, skb->dev, neigh->output);
error:
@@ -577,22 +481,6 @@ drop:
return err;
}
-
-/*
- * Simple frontend to the l2 routing function which filters
- * traffic not in our area when we should only do l1
- * routing.
- */
-static int dn_l1_forward(struct sk_buff *skb)
-{
- struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
-
- if ((dn_ntohs(cb->dst ^ decnet_address) & 0xfc00) == 0)
- return dn_l2_forward(skb);
-
- kfree_skb(skb);
- return 0;
-}
#endif
/*
@@ -624,46 +512,61 @@ static int dn_rt_bug(struct sk_buff *skb)
return -EINVAL;
}
-static int dn_route_output_slow(struct sock *sk)
+static int dn_route_output_slow(struct dst_entry **pprt, dn_address dst, dn_address src, int flags)
{
- struct dn_scp *scp = &sk->protinfo.dn;
- dn_address dest = dn_saddr2dn(&scp->peer);
struct dn_route *rt = NULL;
- struct device *dev = decnet_default_device;
+ struct net_device *dev = decnet_default_device;
struct neighbour *neigh = NULL;
struct dn_dev *dn_db;
- unsigned char addr[6];
+
#ifdef CONFIG_DECNET_ROUTER
- if ((decnet_node_type == DN_RT_INFO_L1RT) || (decnet_node_type == DN_RT_INFO_L2RT)) {
-#if 0
- struct dn_fib_ent *fe = dn_fib_lookup(dest, decnet_address);
+ if (decnet_node_type != DN_RT_INFO_ENDN) {
+ struct dn_fib_res res;
+ int err = 0;
- if (fe != NULL) {
- neigh = neigh_clone(fe->neigh);
- dn_fib_release(fe);
- goto got_route;
+ res.res_addr = dst;
+ res.res_mask = 0;
+ res.res_ifindex = 0;
+ res.res_proto = RTN_UNSPEC;
+ res.res_cost = 0;
+
+ if ((err = dn_fib_resolve(&res)) == 0) {
+
+ if (!res.res_fa || (res.res_fa->fa_type != RTN_UNICAST))
+ return -EPROTO;
+
+ if ((neigh = neigh_clone(res.res_fa->fa_neigh)) != NULL)
+ goto got_route;
+
+ return -ENOBUFS;
}
-#endif
+
+ if (err != -ENOENT)
+ return err;
}
#endif
- dn_dn2eth(addr, dest);
-
/* Look in On-Ethernet cache first */
- if ((neigh = dn_neigh_lookup(&dn_neigh_table, &addr)) != NULL)
- goto got_route;
+ if (!(flags & MSG_TRYHARD)) {
+ if ((neigh = dn_neigh_lookup(&dn_neigh_table, &dst)) != NULL)
+ goto got_route;
+ }
if (dev == NULL)
return -EINVAL;
- /* FIXME: We need to change this for routing nodes */
- /* Send to default router if that doesn't work */
- if ((neigh = neigh_lookup(&dn_neigh_table, &addr, dev)) != NULL)
+ dn_db = dev->dn_ptr;
+
+ if (dn_db == NULL)
+ return -EINVAL;
+
+ /* Try default router */
+ if ((neigh = neigh_clone(dn_db->router)) != NULL)
goto got_route;
/* Send to default device (and hope for the best) if above fail */
- if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) != NULL)
+ if ((neigh = __neigh_lookup(&dn_neigh_table, &dst, dev, 1)) != NULL)
goto got_route;
@@ -671,15 +574,15 @@ static int dn_route_output_slow(struct sock *sk)
got_route:
- if ((rt = dst_alloc(sizeof(struct dn_route), &dn_dst_ops)) == NULL) {
+ if ((rt = dst_alloc(&dn_dst_ops)) == NULL) {
neigh_release(neigh);
return -EINVAL;
}
dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
- rt->rt_saddr = decnet_address;
- rt->rt_daddr = dest;
+ rt->rt_saddr = src;
+ rt->rt_daddr = dst;
rt->rt_oif = neigh->dev->ifindex;
rt->rt_iif = 0;
@@ -693,45 +596,52 @@ got_route:
rt->u.dst.input = dn_nsp_rx;
dn_insert_route(rt);
- sk->dst_cache = &rt->u.dst;
+ *pprt = &rt->u.dst;
return 0;
}
-int dn_route_output(struct sock *sk)
+int dn_route_output(struct dst_entry **pprt, dn_address dst, dn_address src, int flags)
{
- struct dn_scp *scp = &sk->protinfo.dn;
- dn_address dest = dn_saddr2dn(&scp->peer);
- unsigned hash = dn_hash(dest);
+ unsigned hash = dn_hash(dst);
struct dn_route *rt = NULL;
- unsigned short src = dn_saddr2dn(&scp->addr);
-
- start_bh_atomic();
- for(rt = dn_route_cache[hash]; rt; rt = rt->u.rt_next) {
- if ((dest == rt->rt_daddr) &&
- (src == rt->rt_saddr) &&
- (rt->rt_iif == 0) &&
- (rt->rt_oif != 0)) {
- rt->u.dst.lastuse = jiffies;
- atomic_inc(&rt->u.dst.use);
- atomic_inc(&rt->u.dst.refcnt);
- end_bh_atomic();
- sk->dst_cache = &rt->u.dst;
- return 0;
+
+ if (!(flags & MSG_TRYHARD)) {
+ read_lock_bh(&dn_hash_lock);
+ for(rt = dn_route_cache[hash]; rt; rt = rt->u.rt_next) {
+ if ((dst == rt->rt_daddr) &&
+ (src == rt->rt_saddr) &&
+ (rt->rt_iif == 0) &&
+ (rt->rt_oif != 0)) {
+ rt->u.dst.lastuse = jiffies;
+ dst_hold(&rt->u.dst);
+ rt->u.dst.__use++;
+ read_unlock_bh(&dn_hash_lock);
+ *pprt = &rt->u.dst;
+ return 0;
+ }
}
+ read_unlock_bh(&dn_hash_lock);
}
- end_bh_atomic();
- return dn_route_output_slow(sk);
+ return dn_route_output_slow(pprt, dst, src, flags);
}
static int dn_route_input_slow(struct sk_buff *skb)
{
struct dn_route *rt = NULL;
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
- struct device *dev = skb->dev;
+ struct net_device *dev = skb->dev;
+ struct dn_dev *dn_db;
struct neighbour *neigh = NULL;
- unsigned char addr[6];
+ int (*dnrt_input)(struct sk_buff *skb);
+ int (*dnrt_output)(struct sk_buff *skb);
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ if ((dn_db = dev->dn_ptr) == NULL)
+ return -EINVAL;
/*
* In this case we've just received a packet from a source
@@ -741,44 +651,56 @@ static int dn_route_input_slow(struct sk_buff *skb)
* so this only affects packets which have originated elsewhere.
*/
if (dn_dev_islocal(dev, cb->src))
- return 1;
+ return -ENOTUNIQ;
-#ifdef CONFIG_DECNET_ROUTER
- if ((decnet_node_type == DN_RT_INFO_L1RT) || (decnet_node_type == DN_RT_INFO_L2RT)) {
-#if 0
- struct dn_fib_ent *fe = NULL;
+ /*
+ * Default is to create a drop everything entry
+ */
+ dnrt_input = dn_blackhole;
+ dnrt_output = dn_rt_bug;
- fe = dn_fib_lookup(cb->src, cb->dst);
+ /*
+ * Is the destination us ?
+ */
+ if (!dn_dev_islocal(dev, cb->dst))
+ goto non_local_input;
- /* Try routing table first */
- if (fe != NULL) {
- neigh = neigh_clone(fe->neigh);
- dn_fib_release(fe);
- goto got_route;
- }
-#endif
- }
-#endif
+ /*
+ * Local input... find source of skb
+ */
+ dnrt_input = dn_nsp_rx;
+ dnrt_output = dn_output;
- dn_dn2eth(addr, cb->src);
+ if ((neigh = neigh_lookup(&dn_neigh_table, &cb->src, dev)) != NULL)
+ goto add_entry;
- /* Now see if we are directly connected */
- if ((neigh = dn_neigh_lookup(&dn_neigh_table, &addr)) != NULL)
- goto got_route;
+ if (dn_db->router && ((neigh = neigh_clone(dn_db->router)) != NULL))
+ goto add_entry;
- if (dev == NULL)
- return -EINVAL;
+ if ((neigh = neigh_create(&dn_neigh_table, &cb->src, dev)) != NULL) {
+ if (dev->type == ARPHRD_ETHER)
+ memcpy(neigh->ha, skb->mac.ethernet->h_source, ETH_ALEN);
+ goto add_entry;
+ }
- /* FIXME: Try the default router here .... */
+ return -ENOBUFS;
- if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) != NULL)
- goto got_route;
+non_local_input:
- return -EINVAL;
+#ifdef CONFIG_DECNET_ROUTER
+ /*
+ * Destination is another node... find next hop in
+ * routing table here.
+ */
+ if (decnet_node_type == DN_RT_INFO_ENDN)
+ goto add_entry;
-got_route:
- if ((rt = dst_alloc(sizeof(struct dn_route), &dn_dst_ops)) == NULL) {
+#endif /* CONFIG_DECNET_ROUTER */
+
+add_entry:
+
+ if ((rt = dst_alloc(&dn_dst_ops)) == NULL) {
neigh_release(neigh);
return -EINVAL;
}
@@ -786,33 +708,13 @@ got_route:
rt->rt_saddr = cb->dst;
rt->rt_daddr = cb->src;
rt->rt_oif = 0;
- rt->rt_iif = neigh->dev->ifindex;
+ rt->rt_iif = dev->ifindex;
rt->u.dst.neighbour = neigh;
- rt->u.dst.dev = neigh->dev;
+ rt->u.dst.dev = dev;
rt->u.dst.lastuse = jiffies;
- rt->u.dst.output = dn_output;
-
- switch(decnet_node_type) {
- case DN_RT_INFO_ENDN:
- rt->u.dst.input = dn_blackhole;
- break;
-#ifdef CONFIG_DECNET_ROUTER
- case DN_RT_INFO_L1RT:
- rt->u.dst.input = dn_l1_forward;
- break;
- case DN_RT_INFO_L2RT:
- rt->u.dst.input = dn_l2_forward;
- break;
-#endif /* CONFIG_DECNET_ROUTER */
- default:
- rt->u.dst.input = dn_blackhole;
- if (net_ratelimit())
- printk(KERN_DEBUG "dn_route_input_slow: What kind of node are we?\n");
- }
-
- if (dn_dev_islocal(dev, cb->dst))
- rt->u.dst.input = dn_nsp_rx;
+ rt->u.dst.output = dnrt_output;
+ rt->u.dst.input = dnrt_input;
dn_insert_route(rt);
skb->dst = (struct dst_entry *)rt;
@@ -829,23 +731,25 @@ int dn_route_input(struct sk_buff *skb)
if (skb->dst)
return 0;
+ read_lock_bh(&dn_hash_lock);
for(rt = dn_route_cache[hash]; rt != NULL; rt = rt->u.rt_next) {
if ((rt->rt_saddr == cb->dst) &&
(rt->rt_daddr == cb->src) &&
(rt->rt_oif == 0) &&
(rt->rt_iif == cb->iif)) {
rt->u.dst.lastuse = jiffies;
- atomic_inc(&rt->u.dst.use);
- atomic_inc(&rt->u.dst.refcnt);
+ dst_hold(&rt->u.dst);
+ rt->u.dst.__use++;
+ read_unlock_bh(&dn_hash_lock);
skb->dst = (struct dst_entry *)rt;
return 0;
}
}
+ read_unlock_bh(&dn_hash_lock);
return dn_route_input_slow(skb);
}
-#ifdef CONFIG_DECNET_ROUTER
#ifdef CONFIG_RTNETLINK
static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait)
{
@@ -863,6 +767,7 @@ static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int
r->rtm_tos = 0;
r->rtm_table = 0;
r->rtm_type = 0;
+ r->rtm_flags = 0;
r->rtm_scope = RT_SCOPE_UNIVERSE;
r->rtm_protocol = RTPROT_UNSPEC;
RTA_PUT(skb, RTA_DST, 2, &rt->rt_daddr);
@@ -883,11 +788,14 @@ rtattr_failure:
return -1;
}
-int dn_fib_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
+/*
+ * This is called by both endnodes and routers now.
+ */
+int dn_cache_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
{
struct rtattr **rta = arg;
- /* struct rtmsg *rtm = NLMSG_DATA(nlh); */
struct dn_route *rt = NULL;
+ struct dn_skb_cb *cb;
dn_address dst = 0;
dn_address src = 0;
int iif = 0;
@@ -898,6 +806,7 @@ int dn_fib_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
if (skb == NULL)
return -ENOBUFS;
skb->mac.raw = skb->data;
+ cb = (struct dn_skb_cb *)skb->cb;
if (rta[RTA_SRC-1])
memcpy(&src, RTA_DATA(rta[RTA_SRC-1]), 2);
@@ -907,29 +816,36 @@ int dn_fib_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
if (iif) {
- struct device *dev;
- if ((dev = dev_get_by_index(iif)) == NULL)
+ struct net_device *dev;
+ if ((dev = dev_get_by_index(iif)) == NULL) {
+ kfree_skb(skb);
return -ENODEV;
- if (!dev->dn_ptr)
+ }
+ if (!dev->dn_ptr) {
+ dev_put(dev);
+ kfree_skb(skb);
return -ENODEV;
+ }
skb->protocol = __constant_htons(ETH_P_DNA_RT);
skb->dev = dev;
- start_bh_atomic();
+ skb->rx_dev = dev;
+ cb->src = src;
+ cb->dst = dst;
err = dn_route_input(skb);
- end_bh_atomic();
+ memset(cb, 0, sizeof(struct dn_skb_cb));
rt = (struct dn_route *)skb->dst;
- if (!err && rt->u.dst.error)
- err = rt->u.dst.error;
} else {
- int oif = 0;
- if (rta[RTA_OIF-1])
- memcpy(&oif, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
- err = -EOPNOTSUPP;
- }
- if (err) {
- kfree_skb(skb);
- return err;
+ err = dn_route_output((struct dst_entry **)&rt, dst, src, 0);
}
+
+ if (!err && rt->u.dst.error)
+ err = rt->u.dst.error;
+ if (skb->dev)
+ dev_put(skb->dev);
+ skb->dev = NULL;
+ skb->rx_dev = NULL;
+ if (err)
+ goto out_free;
skb->dst = &rt->u.dst;
NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
@@ -937,16 +853,65 @@ int dn_fib_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0);
if (err == 0)
- return 0;
- if (err < 0)
- return -EMSGSIZE;
+ goto out_free;
+ if (err < 0) {
+ err = -EMSGSIZE;
+ goto out_free;
+ }
err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
return err;
+
+out_free:
+ kfree_skb(skb);
+ return err;
+}
+
+/*
+ * For routers, this is called from dn_fib_dump, but for endnodes its
+ * called directly from the rtnetlink dispatch table.
+ */
+int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct dn_route *rt;
+ int h, s_h;
+ int idx, s_idx;
+
+ if (NLMSG_PAYLOAD(cb->nlh, 0) < sizeof(struct rtmsg))
+ return -EINVAL;
+ if (!(((struct rtmsg *)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED))
+ return 0;
+
+ s_h = cb->args[0];
+ s_idx = idx = cb->args[1];
+ for(h = 0; h < DN_HASHBUCKETS; h++) {
+ if (h < s_h)
+ continue;
+ if (h > s_h)
+ s_idx = 0;
+ read_lock_bh(&dn_hash_lock);
+ for(rt = dn_route_cache[h], idx = 0; rt; rt = rt->u.rt_next, idx++) {
+ if (idx < s_idx)
+ continue;
+ skb->dst = dst_clone(&rt->u.dst);
+ if (dn_rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) {
+ dst_release(xchg(&skb->dst, NULL));
+ read_unlock_bh(&dn_hash_lock);
+ goto done;
+ }
+ dst_release(xchg(&skb->dst, NULL));
+ }
+ read_unlock_bh(&dn_hash_lock);
+ }
+
+done:
+ cb->args[0] = h;
+ cb->args[1] = idx;
+ return skb->len;
}
#endif /* CONFIG_RTNETLINK */
-#endif /* CONFIG_DECNET_ROUTER */
#ifdef CONFIG_PROC_FS
@@ -959,7 +924,7 @@ static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int l
int i;
char buf1[DN_ASCBUF_LEN], buf2[DN_ASCBUF_LEN];
- start_bh_atomic();
+ read_lock_bh(&dn_hash_lock);
for(i = 0; i < DN_HASHBUCKETS; i++) {
rt = dn_route_cache[i];
for(; rt != NULL; rt = rt->u.rt_next) {
@@ -967,8 +932,8 @@ static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int l
rt->u.dst.dev ? rt->u.dst.dev->name : "*",
dn_addr2asc(dn_ntohs(rt->rt_daddr), buf1),
dn_addr2asc(dn_ntohs(rt->rt_saddr), buf2),
- atomic_read(&rt->u.dst.use),
- atomic_read(&rt->u.dst.refcnt),
+ atomic_read(&rt->u.dst.__refcnt),
+ rt->u.dst.__use,
(int)rt->u.dst.rtt
);
@@ -984,7 +949,7 @@ static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int l
if (pos > offset + length)
break;
}
- end_bh_atomic();
+ read_unlock_bh(&dn_hash_lock);
*start = buffer + (offset - begin);
len -= (offset - begin);
@@ -1007,6 +972,11 @@ void __init dn_route_init(void)
{
memset(dn_route_cache, 0, sizeof(struct dn_route *) * DN_HASHBUCKETS);
+ dn_dst_ops.kmem_cachep = kmem_cache_create("dn_dst_cache",
+ sizeof(struct dn_route),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+
dn_route_timer.function = dn_dst_check_expire;
dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ;
add_timer(&dn_route_timer);