@ -1529,25 +1529,6 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root,
fib6_walk ( & c . w ) ;
}
void fib6_clean_all_ro ( struct net * net , int ( * func ) ( struct rt6_info * , void * arg ) ,
int prune , void * arg )
{
struct fib6_table * table ;
struct hlist_head * head ;
unsigned int h ;
rcu_read_lock ( ) ;
for ( h = 0 ; h < FIB6_TABLE_HASHSZ ; h + + ) {
head = & net - > ipv6 . fib_table_hash [ h ] ;
hlist_for_each_entry_rcu ( table , head , tb6_hlist ) {
read_lock_bh ( & table - > tb6_lock ) ;
fib6_clean_tree ( net , & table - > tb6_root ,
func , prune , arg ) ;
read_unlock_bh ( & table - > tb6_lock ) ;
}
}
rcu_read_unlock ( ) ;
}
void fib6_clean_all ( struct net * net , int ( * func ) ( struct rt6_info * , void * arg ) ,
int prune , void * arg )
{
@ -1782,3 +1763,175 @@ void fib6_gc_cleanup(void)
unregister_pernet_subsys ( & fib6_net_ops ) ;
kmem_cache_destroy ( fib6_node_kmem ) ;
}
# ifdef CONFIG_PROC_FS
struct ipv6_route_iter {
struct seq_net_private p ;
struct fib6_walker_t w ;
loff_t skip ;
struct fib6_table * tbl ;
} ;
static int ipv6_route_seq_show ( struct seq_file * seq , void * v )
{
struct rt6_info * rt = v ;
struct ipv6_route_iter * iter = seq - > private ;
seq_printf ( seq , " %pi6 %02x " , & rt - > rt6i_dst . addr , rt - > rt6i_dst . plen ) ;
# ifdef CONFIG_IPV6_SUBTREES
seq_printf ( seq , " %pi6 %02x " , & rt - > rt6i_src . addr , rt - > rt6i_src . plen ) ;
# else
seq_puts ( seq , " 00000000000000000000000000000000 00 " ) ;
# endif
if ( rt - > rt6i_flags & RTF_GATEWAY )
seq_printf ( seq , " %pi6 " , & rt - > rt6i_gateway ) ;
else
seq_puts ( seq , " 00000000000000000000000000000000 " ) ;
seq_printf ( seq , " %08x %08x %08x %08x %8s \n " ,
rt - > rt6i_metric , atomic_read ( & rt - > dst . __refcnt ) ,
rt - > dst . __use , rt - > rt6i_flags ,
rt - > dst . dev ? rt - > dst . dev - > name : " " ) ;
iter - > w . leaf = NULL ;
return 0 ;
}
static int ipv6_route_yield ( struct fib6_walker_t * w )
{
struct ipv6_route_iter * iter = w - > args ;
if ( ! iter - > skip )
return 1 ;
do {
iter - > w . leaf = iter - > w . leaf - > dst . rt6_next ;
iter - > skip - - ;
if ( ! iter - > skip & & iter - > w . leaf )
return 1 ;
} while ( iter - > w . leaf ) ;
return 0 ;
}
static void ipv6_route_seq_setup_walk ( struct ipv6_route_iter * iter )
{
memset ( & iter - > w , 0 , sizeof ( iter - > w ) ) ;
iter - > w . func = ipv6_route_yield ;
iter - > w . root = & iter - > tbl - > tb6_root ;
iter - > w . state = FWS_INIT ;
iter - > w . node = iter - > w . root ;
iter - > w . args = iter ;
INIT_LIST_HEAD ( & iter - > w . lh ) ;
fib6_walker_link ( & iter - > w ) ;
}
static struct fib6_table * ipv6_route_seq_next_table ( struct fib6_table * tbl ,
struct net * net )
{
unsigned int h ;
struct hlist_node * node ;
if ( tbl ) {
h = ( tbl - > tb6_id & ( FIB6_TABLE_HASHSZ - 1 ) ) + 1 ;
node = rcu_dereference_bh ( hlist_next_rcu ( & tbl - > tb6_hlist ) ) ;
} else {
h = 0 ;
node = NULL ;
}
while ( ! node & & h < FIB6_TABLE_HASHSZ ) {
node = rcu_dereference_bh (
hlist_first_rcu ( & net - > ipv6 . fib_table_hash [ h + + ] ) ) ;
}
return hlist_entry_safe ( node , struct fib6_table , tb6_hlist ) ;
}
static void * ipv6_route_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
int r ;
struct rt6_info * n ;
struct net * net = seq_file_net ( seq ) ;
struct ipv6_route_iter * iter = seq - > private ;
if ( ! v )
goto iter_table ;
n = ( ( struct rt6_info * ) v ) - > dst . rt6_next ;
if ( n ) {
+ + * pos ;
return n ;
}
iter_table :
read_lock ( & iter - > tbl - > tb6_lock ) ;
r = fib6_walk_continue ( & iter - > w ) ;
read_unlock ( & iter - > tbl - > tb6_lock ) ;
if ( r > 0 ) {
if ( v )
+ + * pos ;
return iter - > w . leaf ;
} else if ( r < 0 ) {
fib6_walker_unlink ( & iter - > w ) ;
return NULL ;
}
fib6_walker_unlink ( & iter - > w ) ;
iter - > tbl = ipv6_route_seq_next_table ( iter - > tbl , net ) ;
if ( ! iter - > tbl )
return NULL ;
ipv6_route_seq_setup_walk ( iter ) ;
goto iter_table ;
}
static void * ipv6_route_seq_start ( struct seq_file * seq , loff_t * pos )
__acquires ( RCU_BH )
{
struct net * net = seq_file_net ( seq ) ;
struct ipv6_route_iter * iter = seq - > private ;
rcu_read_lock_bh ( ) ;
iter - > tbl = ipv6_route_seq_next_table ( NULL , net ) ;
iter - > skip = * pos ;
if ( iter - > tbl ) {
ipv6_route_seq_setup_walk ( iter ) ;
return ipv6_route_seq_next ( seq , NULL , pos ) ;
} else {
return NULL ;
}
}
static bool ipv6_route_iter_active ( struct ipv6_route_iter * iter )
{
struct fib6_walker_t * w = & iter - > w ;
return w - > node & & ! ( w - > state = = FWS_U & & w - > node = = w - > root ) ;
}
static void ipv6_route_seq_stop ( struct seq_file * seq , void * v )
__releases ( RCU_BH )
{
struct ipv6_route_iter * iter = seq - > private ;
if ( ipv6_route_iter_active ( iter ) )
fib6_walker_unlink ( & iter - > w ) ;
rcu_read_unlock_bh ( ) ;
}
static const struct seq_operations ipv6_route_seq_ops = {
. start = ipv6_route_seq_start ,
. next = ipv6_route_seq_next ,
. stop = ipv6_route_seq_stop ,
. show = ipv6_route_seq_show
} ;
int ipv6_route_open ( struct inode * inode , struct file * file )
{
return seq_open_net ( inode , file , & ipv6_route_seq_ops ,
sizeof ( struct ipv6_route_iter ) ) ;
}
# endif /* CONFIG_PROC_FS */