Commit 006f68b8 authored by Ralf Baechle DL5RB's avatar Ralf Baechle DL5RB Committed by David S. Miller

[AX.25]: Reference counting for AX.25 routes.

In the past routes could be freed even though the were possibly in use ...
Signed-off-by: default avatarRalf Baechle DL5RB <ralf@linux-mips.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8dc22d2b
...@@ -182,14 +182,26 @@ typedef struct { ...@@ -182,14 +182,26 @@ typedef struct {
typedef struct ax25_route { typedef struct ax25_route {
struct ax25_route *next; struct ax25_route *next;
atomic_t ref; atomic_t refcount;
ax25_address callsign; ax25_address callsign;
struct net_device *dev; struct net_device *dev;
ax25_digi *digipeat; ax25_digi *digipeat;
char ip_mode; char ip_mode;
struct timer_list timer;
} ax25_route; } ax25_route;
static inline void ax25_hold_route(ax25_route *ax25_rt)
{
atomic_inc(&ax25_rt->refcount);
}
extern void __ax25_put_route(ax25_route *ax25_rt);
static inline void ax25_put_route(ax25_route *ax25_rt)
{
if (atomic_dec_and_test(&ax25_rt->refcount))
__ax25_put_route(ax25_rt);
}
typedef struct { typedef struct {
char slave; /* slave_mode? */ char slave; /* slave_mode? */
struct timer_list slave_timer; /* timeout timer */ struct timer_list slave_timer; /* timeout timer */
...@@ -348,17 +360,11 @@ extern int ax25_check_iframes_acked(ax25_cb *, unsigned short); ...@@ -348,17 +360,11 @@ extern int ax25_check_iframes_acked(ax25_cb *, unsigned short);
extern void ax25_rt_device_down(struct net_device *); extern void ax25_rt_device_down(struct net_device *);
extern int ax25_rt_ioctl(unsigned int, void __user *); extern int ax25_rt_ioctl(unsigned int, void __user *);
extern struct file_operations ax25_route_fops; extern struct file_operations ax25_route_fops;
extern ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev);
extern int ax25_rt_autobind(ax25_cb *, ax25_address *); extern int ax25_rt_autobind(ax25_cb *, ax25_address *);
extern ax25_route *ax25_rt_find_route(ax25_route *, ax25_address *,
struct net_device *);
extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *);
extern void ax25_rt_free(void); extern void ax25_rt_free(void);
static inline void ax25_put_route(ax25_route *ax25_rt)
{
atomic_dec(&ax25_rt->ref);
}
/* ax25_std_in.c */ /* ax25_std_in.c */
extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int);
......
...@@ -103,11 +103,13 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -103,11 +103,13 @@ int ax25_rebuild_header(struct sk_buff *skb)
{ {
struct sk_buff *ourskb; struct sk_buff *ourskb;
unsigned char *bp = skb->data; unsigned char *bp = skb->data;
struct net_device *dev; ax25_route *route;
struct net_device *dev = NULL;
ax25_address *src, *dst; ax25_address *src, *dst;
ax25_digi *digipeat = NULL;
ax25_dev *ax25_dev; ax25_dev *ax25_dev;
ax25_route _route, *route = &_route;
ax25_cb *ax25; ax25_cb *ax25;
char ip_mode = ' ';
dst = (ax25_address *)(bp + 1); dst = (ax25_address *)(bp + 1);
src = (ax25_address *)(bp + 8); src = (ax25_address *)(bp + 8);
...@@ -115,8 +117,12 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -115,8 +117,12 @@ int ax25_rebuild_header(struct sk_buff *skb)
if (arp_find(bp + 1, skb)) if (arp_find(bp + 1, skb))
return 1; return 1;
route = ax25_rt_find_route(route, dst, NULL); route = ax25_get_route(dst, NULL);
dev = route->dev; if (route) {
digipeat = route->digipeat;
dev = route->dev;
ip_mode = route->ip_mode;
};
if (dev == NULL) if (dev == NULL)
dev = skb->dev; dev = skb->dev;
...@@ -126,7 +132,7 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -126,7 +132,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
} }
if (bp[16] == AX25_P_IP) { if (bp[16] == AX25_P_IP) {
if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { if (ip_mode == 'V' || (ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
/* /*
* We copy the buffer and release the original thereby * We copy the buffer and release the original thereby
* keeping it straight * keeping it straight
...@@ -172,7 +178,7 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -172,7 +178,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
ourskb, ourskb,
ax25_dev->values[AX25_VALUES_PACLEN], ax25_dev->values[AX25_VALUES_PACLEN],
&src_c, &src_c,
&dst_c, route->digipeat, dev); &dst_c, digipeat, dev);
if (ax25) { if (ax25) {
ax25_cb_put(ax25); ax25_cb_put(ax25);
} }
...@@ -190,7 +196,7 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -190,7 +196,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
skb_pull(skb, AX25_KISS_HEADER_LEN); skb_pull(skb, AX25_KISS_HEADER_LEN);
if (route->digipeat != NULL) { if (digipeat != NULL) {
if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
kfree_skb(skb); kfree_skb(skb);
goto put; goto put;
...@@ -202,7 +208,8 @@ int ax25_rebuild_header(struct sk_buff *skb) ...@@ -202,7 +208,8 @@ int ax25_rebuild_header(struct sk_buff *skb)
ax25_queue_xmit(skb, dev); ax25_queue_xmit(skb, dev);
put: put:
ax25_put_route(route); if (route)
ax25_put_route(route);
return 1; return 1;
} }
......
...@@ -41,8 +41,6 @@ ...@@ -41,8 +41,6 @@
static ax25_route *ax25_route_list; static ax25_route *ax25_route_list;
static DEFINE_RWLOCK(ax25_route_lock); static DEFINE_RWLOCK(ax25_route_lock);
static ax25_route *ax25_get_route(ax25_address *, struct net_device *);
void ax25_rt_device_down(struct net_device *dev) void ax25_rt_device_down(struct net_device *dev)
{ {
ax25_route *s, *t, *ax25_rt; ax25_route *s, *t, *ax25_rt;
...@@ -115,7 +113,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route) ...@@ -115,7 +113,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
return -ENOMEM; return -ENOMEM;
} }
atomic_set(&ax25_rt->ref, 0); atomic_set(&ax25_rt->refcount, 1);
ax25_rt->callsign = route->dest_addr; ax25_rt->callsign = route->dest_addr;
ax25_rt->dev = ax25_dev->dev; ax25_rt->dev = ax25_dev->dev;
ax25_rt->digipeat = NULL; ax25_rt->digipeat = NULL;
...@@ -140,23 +138,10 @@ static int ax25_rt_add(struct ax25_routes_struct *route) ...@@ -140,23 +138,10 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
return 0; return 0;
} }
static void ax25_rt_destroy(ax25_route *ax25_rt) void __ax25_put_route(ax25_route *ax25_rt)
{ {
if (atomic_read(&ax25_rt->ref) == 0) { kfree(ax25_rt->digipeat);
kfree(ax25_rt->digipeat); kfree(ax25_rt);
kfree(ax25_rt);
return;
}
/*
* Uh... Route is still in use; we can't yet destroy it. Retry later.
*/
init_timer(&ax25_rt->timer);
ax25_rt->timer.data = (unsigned long) ax25_rt;
ax25_rt->timer.function = (void *) ax25_rt_destroy;
ax25_rt->timer.expires = jiffies + 5 * HZ;
add_timer(&ax25_rt->timer);
} }
static int ax25_rt_del(struct ax25_routes_struct *route) static int ax25_rt_del(struct ax25_routes_struct *route)
...@@ -177,12 +162,12 @@ static int ax25_rt_del(struct ax25_routes_struct *route) ...@@ -177,12 +162,12 @@ static int ax25_rt_del(struct ax25_routes_struct *route)
ax25cmp(&route->dest_addr, &s->callsign) == 0) { ax25cmp(&route->dest_addr, &s->callsign) == 0) {
if (ax25_route_list == s) { if (ax25_route_list == s) {
ax25_route_list = s->next; ax25_route_list = s->next;
ax25_rt_destroy(s); ax25_put_route(s);
} else { } else {
for (t = ax25_route_list; t != NULL; t = t->next) { for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) { if (t->next == s) {
t->next = s->next; t->next = s->next;
ax25_rt_destroy(s); ax25_put_route(s);
break; break;
} }
} }
...@@ -362,7 +347,7 @@ struct file_operations ax25_route_fops = { ...@@ -362,7 +347,7 @@ struct file_operations ax25_route_fops = {
* *
* Only routes with a reference count of zero can be destroyed. * Only routes with a reference count of zero can be destroyed.
*/ */
static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
{ {
ax25_route *ax25_spe_rt = NULL; ax25_route *ax25_spe_rt = NULL;
ax25_route *ax25_def_rt = NULL; ax25_route *ax25_def_rt = NULL;
...@@ -392,7 +377,7 @@ static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) ...@@ -392,7 +377,7 @@ static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
ax25_rt = ax25_spe_rt; ax25_rt = ax25_spe_rt;
if (ax25_rt != NULL) if (ax25_rt != NULL)
atomic_inc(&ax25_rt->ref); ax25_hold_route(ax25_rt);
read_unlock(&ax25_route_lock); read_unlock(&ax25_route_lock);
...@@ -467,24 +452,6 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) ...@@ -467,24 +452,6 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
return 0; return 0;
} }
ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr,
struct net_device *dev)
{
ax25_route *ax25_rt;
if ((ax25_rt = ax25_get_route(addr, dev)))
return ax25_rt;
route->next = NULL;
atomic_set(&route->ref, 1);
route->callsign = *addr;
route->dev = dev;
route->digipeat = NULL;
route->ip_mode = ' ';
return route;
}
struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src,
ax25_address *dest, ax25_digi *digi) ax25_address *dest, ax25_digi *digi)
{ {
......
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