Skip to content

Commit

Permalink
[AX.25]: Reference counting for AX.25 routes.
Browse files Browse the repository at this point in the history
In the past routes could be freed even though the were possibly in use ...

Signed-off-by: Ralf Baechle DL5RB <ralf@linux-mips.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Ralf Baechle DL5RB authored and David S. Miller committed Jul 4, 2006
1 parent 8dc22d2 commit 006f68b
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 58 deletions.
24 changes: 15 additions & 9 deletions include/net/ax25.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,26 @@ typedef struct {

typedef struct ax25_route {
struct ax25_route *next;
atomic_t ref;
atomic_t refcount;
ax25_address callsign;
struct net_device *dev;
ax25_digi *digipeat;
char ip_mode;
struct timer_list timer;
} 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 {
char slave; /* slave_mode? */
struct timer_list slave_timer; /* timeout timer */
Expand Down Expand Up @@ -348,17 +360,11 @@ extern int ax25_check_iframes_acked(ax25_cb *, unsigned short);
extern void ax25_rt_device_down(struct net_device *);
extern int ax25_rt_ioctl(unsigned int, void __user *);
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 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 void ax25_rt_free(void);

static inline void ax25_put_route(ax25_route *ax25_rt)
{
atomic_dec(&ax25_rt->ref);
}

/* ax25_std_in.c */
extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int);

Expand Down
23 changes: 15 additions & 8 deletions net/ax25/ax25_ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,26 @@ int ax25_rebuild_header(struct sk_buff *skb)
{
struct sk_buff *ourskb;
unsigned char *bp = skb->data;
struct net_device *dev;
ax25_route *route;
struct net_device *dev = NULL;
ax25_address *src, *dst;
ax25_digi *digipeat = NULL;
ax25_dev *ax25_dev;
ax25_route _route, *route = &_route;
ax25_cb *ax25;
char ip_mode = ' ';

dst = (ax25_address *)(bp + 1);
src = (ax25_address *)(bp + 8);

if (arp_find(bp + 1, skb))
return 1;

route = ax25_rt_find_route(route, dst, NULL);
dev = route->dev;
route = ax25_get_route(dst, NULL);
if (route) {
digipeat = route->digipeat;
dev = route->dev;
ip_mode = route->ip_mode;
};

if (dev == NULL)
dev = skb->dev;
Expand All @@ -126,7 +132,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
}

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
* keeping it straight
Expand Down Expand Up @@ -172,7 +178,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
ourskb,
ax25_dev->values[AX25_VALUES_PACLEN],
&src_c,
&dst_c, route->digipeat, dev);
&dst_c, digipeat, dev);
if (ax25) {
ax25_cb_put(ax25);
}
Expand All @@ -190,7 +196,7 @@ int ax25_rebuild_header(struct sk_buff *skb)

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) {
kfree_skb(skb);
goto put;
Expand All @@ -202,7 +208,8 @@ int ax25_rebuild_header(struct sk_buff *skb)
ax25_queue_xmit(skb, dev);

put:
ax25_put_route(route);
if (route)
ax25_put_route(route);

return 1;
}
Expand Down
49 changes: 8 additions & 41 deletions net/ax25/ax25_route.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@
static ax25_route *ax25_route_list;
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)
{
ax25_route *s, *t, *ax25_rt;
Expand Down Expand Up @@ -115,7 +113,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
return -ENOMEM;
}

atomic_set(&ax25_rt->ref, 0);
atomic_set(&ax25_rt->refcount, 1);
ax25_rt->callsign = route->dest_addr;
ax25_rt->dev = ax25_dev->dev;
ax25_rt->digipeat = NULL;
Expand All @@ -140,23 +138,10 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
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);
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);
kfree(ax25_rt->digipeat);
kfree(ax25_rt);
}

static int ax25_rt_del(struct ax25_routes_struct *route)
Expand All @@ -177,12 +162,12 @@ static int ax25_rt_del(struct ax25_routes_struct *route)
ax25cmp(&route->dest_addr, &s->callsign) == 0) {
if (ax25_route_list == s) {
ax25_route_list = s->next;
ax25_rt_destroy(s);
ax25_put_route(s);
} else {
for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) {
t->next = s->next;
ax25_rt_destroy(s);
ax25_put_route(s);
break;
}
}
Expand Down Expand Up @@ -362,7 +347,7 @@ struct file_operations ax25_route_fops = {
*
* 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_def_rt = NULL;
Expand Down Expand Up @@ -392,7 +377,7 @@ static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
ax25_rt = ax25_spe_rt;

if (ax25_rt != NULL)
atomic_inc(&ax25_rt->ref);
ax25_hold_route(ax25_rt);

read_unlock(&ax25_route_lock);

Expand Down Expand Up @@ -467,24 +452,6 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
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,
ax25_address *dest, ax25_digi *digi)
{
Expand Down

0 comments on commit 006f68b

Please sign in to comment.