2007-02-09 00:34:02 +03:00
/*
* This module :
* This module is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
* History
* 03 - 01 - 2007 Added forwarding for x .25 Andrew Hendry
*/
# include <linux/if_arp.h>
# include <linux/init.h>
# include <net/x25.h>
2007-12-07 11:50:43 +03:00
LIST_HEAD ( x25_forward_list ) ;
2007-02-09 00:34:02 +03:00
DEFINE_RWLOCK ( x25_forward_list_lock ) ;
int x25_forward_call ( struct x25_address * dest_addr , struct x25_neigh * from ,
struct sk_buff * skb , int lci )
{
struct x25_route * rt ;
struct x25_neigh * neigh_new = NULL ;
struct list_head * entry ;
struct x25_forward * x25_frwd , * new_frwd ;
struct sk_buff * skbn ;
short same_lci = 0 ;
int rc = 0 ;
2007-03-20 03:04:56 +03:00
if ( ( rt = x25_get_route ( dest_addr ) ) = = NULL )
goto out_no_route ;
2007-02-09 00:34:02 +03:00
2007-03-20 03:04:56 +03:00
if ( ( neigh_new = x25_get_neigh ( rt - > dev ) ) = = NULL ) {
/* This shouldnt happen, if it occurs somehow
* do something sensible
2007-02-09 00:34:02 +03:00
*/
2007-03-20 03:04:56 +03:00
goto out_put_route ;
}
2007-02-09 00:34:02 +03:00
2007-03-20 03:04:56 +03:00
/* Avoid a loop. This is the normal exit path for a
* system with only one x .25 iface and default route
*/
if ( rt - > dev = = from - > dev ) {
goto out_put_nb ;
}
/* Remote end sending a call request on an already
* established LCI ? It shouldnt happen , just in case . .
*/
read_lock_bh ( & x25_forward_list_lock ) ;
list_for_each ( entry , & x25_forward_list ) {
x25_frwd = list_entry ( entry , struct x25_forward , node ) ;
if ( x25_frwd - > lci = = lci ) {
printk ( KERN_WARNING " X.25: call request for lci which is already registered!, transmitting but not registering new pair \n " ) ;
same_lci = 1 ;
2007-02-09 00:34:02 +03:00
}
2007-03-20 03:04:56 +03:00
}
read_unlock_bh ( & x25_forward_list_lock ) ;
2007-02-09 00:34:02 +03:00
2007-03-20 03:04:56 +03:00
/* Save the forwarding details for future traffic */
if ( ! same_lci ) {
if ( ( new_frwd = kmalloc ( sizeof ( struct x25_forward ) ,
GFP_ATOMIC ) ) = = NULL ) {
rc = - ENOMEM ;
2007-02-09 00:34:02 +03:00
goto out_put_nb ;
}
2007-03-20 03:04:56 +03:00
new_frwd - > lci = lci ;
new_frwd - > dev1 = rt - > dev ;
new_frwd - > dev2 = from - > dev ;
write_lock_bh ( & x25_forward_list_lock ) ;
list_add ( & new_frwd - > node , & x25_forward_list ) ;
write_unlock_bh ( & x25_forward_list_lock ) ;
}
/* Forward the call request */
if ( ( skbn = skb_clone ( skb , GFP_ATOMIC ) ) = = NULL ) {
goto out_put_nb ;
2007-02-09 00:34:02 +03:00
}
2007-03-20 03:04:56 +03:00
x25_transmit_link ( skbn , neigh_new ) ;
rc = 1 ;
2007-02-09 00:34:02 +03:00
out_put_nb :
x25_neigh_put ( neigh_new ) ;
out_put_route :
x25_route_put ( rt ) ;
2007-03-20 03:04:56 +03:00
out_no_route :
2007-02-09 00:34:02 +03:00
return rc ;
}
int x25_forward_data ( int lci , struct x25_neigh * from , struct sk_buff * skb ) {
struct x25_forward * frwd ;
struct list_head * entry ;
struct net_device * peer = NULL ;
struct x25_neigh * nb ;
struct sk_buff * skbn ;
int rc = 0 ;
read_lock_bh ( & x25_forward_list_lock ) ;
list_for_each ( entry , & x25_forward_list ) {
frwd = list_entry ( entry , struct x25_forward , node ) ;
if ( frwd - > lci = = lci ) {
/* The call is established, either side can send */
if ( from - > dev = = frwd - > dev1 ) {
peer = frwd - > dev2 ;
} else {
peer = frwd - > dev1 ;
}
break ;
}
}
read_unlock_bh ( & x25_forward_list_lock ) ;
if ( ( nb = x25_get_neigh ( peer ) ) = = NULL )
goto out ;
if ( ( skbn = pskb_copy ( skb , GFP_ATOMIC ) ) = = NULL ) {
[X25]: Add missing x25_neigh_put
The function x25_get_neigh increments a reference count. At the point of
the second goto out, the result of calling x25_get_neigh is only stored in
a local variable, and thus no one outside the function will be able to
decrease the reference count. Thus, x25_neigh_put should be called before
the return in this case.
The problem was found using the following semantic match.
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@@
type T,T1,T2;
identifier E;
statement S;
expression x1,x2,x3;
int ret;
@@
T E;
...
* if ((E = x25_get_neigh(...)) == NULL)
S
... when != x25_neigh_put(...,(T1)E,...)
when != if (E != NULL) { ... x25_neigh_put(...,(T1)E,...); ...}
when != x1 = (T1)E
when != E = x3;
when any
if (...) {
... when != x25_neigh_put(...,(T2)E,...)
when != if (E != NULL) { ... x25_neigh_put(...,(T2)E,...); ...}
when != x2 = (T2)E
(
* return;
|
* return ret;
)
}
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-01-02 06:30:30 +03:00
goto output ;
2007-02-09 00:34:02 +03:00
}
x25_transmit_link ( skbn , nb ) ;
rc = 1 ;
[X25]: Add missing x25_neigh_put
The function x25_get_neigh increments a reference count. At the point of
the second goto out, the result of calling x25_get_neigh is only stored in
a local variable, and thus no one outside the function will be able to
decrease the reference count. Thus, x25_neigh_put should be called before
the return in this case.
The problem was found using the following semantic match.
(http://www.emn.fr/x-info/coccinelle/)
// <smpl>
@@
type T,T1,T2;
identifier E;
statement S;
expression x1,x2,x3;
int ret;
@@
T E;
...
* if ((E = x25_get_neigh(...)) == NULL)
S
... when != x25_neigh_put(...,(T1)E,...)
when != if (E != NULL) { ... x25_neigh_put(...,(T1)E,...); ...}
when != x1 = (T1)E
when != E = x3;
when any
if (...) {
... when != x25_neigh_put(...,(T2)E,...)
when != if (E != NULL) { ... x25_neigh_put(...,(T2)E,...); ...}
when != x2 = (T2)E
(
* return;
|
* return ret;
)
}
// </smpl>
Signed-off-by: Julia Lawall <julia@diku.dk>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-01-02 06:30:30 +03:00
output :
x25_neigh_put ( nb ) ;
2007-02-09 00:34:02 +03:00
out :
return rc ;
}
void x25_clear_forward_by_lci ( unsigned int lci )
{
struct x25_forward * fwd ;
struct list_head * entry , * tmp ;
write_lock_bh ( & x25_forward_list_lock ) ;
list_for_each_safe ( entry , tmp , & x25_forward_list ) {
fwd = list_entry ( entry , struct x25_forward , node ) ;
if ( fwd - > lci = = lci ) {
list_del ( & fwd - > node ) ;
kfree ( fwd ) ;
}
}
write_unlock_bh ( & x25_forward_list_lock ) ;
}
void x25_clear_forward_by_dev ( struct net_device * dev )
{
struct x25_forward * fwd ;
struct list_head * entry , * tmp ;
write_lock_bh ( & x25_forward_list_lock ) ;
list_for_each_safe ( entry , tmp , & x25_forward_list ) {
fwd = list_entry ( entry , struct x25_forward , node ) ;
if ( ( fwd - > dev1 = = dev ) | | ( fwd - > dev2 = = dev ) ) {
list_del ( & fwd - > node ) ;
kfree ( fwd ) ;
}
}
write_unlock_bh ( & x25_forward_list_lock ) ;
}