2005-04-17 02:20:36 +04:00
/*
* DECnet An implementation of the DECnet protocol suite for the LINUX
* operating system . DECnet is implemented using the BSD Socket
* interface as the means of communication with the user level .
*
* DECnet Routing Functions ( Endnode and Router )
*
* Authors : Steve Whitehouse < SteveW @ ACM . org >
* Eduardo Marcelo Serrat < emserrat @ geocities . com >
*
* Changes :
* Steve Whitehouse : Fixes to allow " intra-ethernet " and
* " return-to-sender " bits on outgoing
* packets .
* 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 .
* Steve Whitehouse : Dave Miller ' s dynamic hash table sizing and
* Alexey Kuznetsov ' s finer grained locking
* from ipv4 / route . c .
* Steve Whitehouse : Routing is now starting to look like a
* sensible set of code now , mainly due to
* my copying the IPv4 routing code . The
* hooks here are modified and will continue
* to evolve for a while .
* Steve Whitehouse : Real SMP at last : - ) Also new netfilter
* stuff . Look out raw sockets your days
* are numbered !
* Steve Whitehouse : Added return - to - sender functions . Added
* backlog congestion level return codes .
* Steve Whitehouse : Fixed bug where routes were set up with
* no ref count on net devices .
* Steve Whitehouse : RCU for the route cache
* Steve Whitehouse : Preparations for the flow cache
* Steve Whitehouse : Prepare for nonlinear skbs
*/
/******************************************************************************
( c ) 1995 - 1998 E . M . Serrat emserrat @ geocities . com
2007-02-09 17:24:40 +03:00
2005-04-17 02:20:36 +04:00
This program 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
any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/socket.h>
# include <linux/in.h>
# include <linux/kernel.h>
# include <linux/sockios.h>
# include <linux/net.h>
# include <linux/netdevice.h>
# include <linux/inet.h>
# include <linux/route.h>
# include <linux/in_route.h>
# include <net/sock.h>
# include <linux/mm.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <linux/init.h>
# include <linux/rtnetlink.h>
# include <linux/string.h>
# include <linux/netfilter_decnet.h>
# include <linux/rcupdate.h>
# include <linux/times.h>
# include <asm/errno.h>
2007-09-12 14:01:34 +04:00
# include <net/net_namespace.h>
2007-03-26 10:06:12 +04:00
# include <net/netlink.h>
2005-04-17 02:20:36 +04:00
# include <net/neighbour.h>
# include <net/dst.h>
# include <net/flow.h>
2006-08-10 02:56:46 +04:00
# include <net/fib_rules.h>
2005-04-17 02:20:36 +04:00
# include <net/dn.h>
# include <net/dn_dev.h>
# include <net/dn_nsp.h>
# include <net/dn_route.h>
# include <net/dn_neigh.h>
# include <net/dn_fib.h>
struct dn_rt_hash_bucket
{
struct dn_route * chain ;
spinlock_t lock ;
} __attribute__ ( ( __aligned__ ( 8 ) ) ) ;
extern struct neigh_table dn_neigh_table ;
static unsigned char dn_hiord_addr [ 6 ] = { 0xAA , 0x00 , 0x04 , 0x00 , 0x00 , 0x00 } ;
static const int dn_rt_min_delay = 2 * HZ ;
static const int dn_rt_max_delay = 10 * HZ ;
static const int dn_rt_mtu_expires = 10 * 60 * HZ ;
static unsigned long dn_rt_deadline ;
static int dn_dst_gc ( void ) ;
static struct dst_entry * dn_dst_check ( struct dst_entry * , __u32 ) ;
static struct dst_entry * dn_dst_negative_advice ( struct dst_entry * ) ;
static void dn_dst_link_failure ( struct sk_buff * ) ;
static void dn_dst_update_pmtu ( struct dst_entry * dst , u32 mtu ) ;
static int dn_route_input ( struct sk_buff * ) ;
static void dn_run_flush ( unsigned long dummy ) ;
static struct dn_rt_hash_bucket * dn_rt_hash_table ;
static unsigned dn_rt_hash_mask ;
static struct timer_list dn_route_timer ;
2005-09-10 00:10:40 +04:00
static DEFINE_TIMER ( dn_rt_flush_timer , dn_run_flush , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
int decnet_dst_gc_interval = 2 ;
static struct dst_ops dn_dst_ops = {
. family = PF_DECnet ,
. protocol = __constant_htons ( ETH_P_DNA_RT ) ,
. gc_thresh = 128 ,
. gc = dn_dst_gc ,
. check = dn_dst_check ,
. negative_advice = dn_dst_negative_advice ,
. link_failure = dn_dst_link_failure ,
. update_pmtu = dn_dst_update_pmtu ,
. entry_size = sizeof ( struct dn_route ) ,
. entries = ATOMIC_INIT ( 0 ) ,
} ;
2006-03-21 09:42:39 +03:00
static __inline__ unsigned dn_hash ( __le16 src , __le16 dst )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:42:39 +03:00
__u16 tmp = ( __u16 __force ) ( src ^ dst ) ;
2005-04-17 02:20:36 +04:00
tmp ^ = ( tmp > > 3 ) ;
tmp ^ = ( tmp > > 5 ) ;
tmp ^ = ( tmp > > 10 ) ;
return dn_rt_hash_mask & ( unsigned ) tmp ;
}
static inline void dnrt_free ( struct dn_route * rt )
{
call_rcu_bh ( & rt - > u . dst . rcu_head , dst_rcu_free ) ;
}
static inline void dnrt_drop ( struct dn_route * rt )
{
2006-03-21 10:00:29 +03:00
dst_release ( & rt - > u . dst ) ;
2005-04-17 02:20:36 +04:00
call_rcu_bh ( & rt - > u . dst . rcu_head , dst_rcu_free ) ;
}
static void dn_dst_check_expire ( unsigned long dummy )
{
int i ;
struct dn_route * rt , * * rtp ;
unsigned long now = jiffies ;
unsigned long expire = 120 * HZ ;
for ( i = 0 ; i < = dn_rt_hash_mask ; i + + ) {
rtp = & dn_rt_hash_table [ i ] . chain ;
spin_lock ( & dn_rt_hash_table [ i ] . lock ) ;
while ( ( rt = * rtp ) ! = NULL ) {
if ( atomic_read ( & rt - > u . dst . __refcnt ) | |
( now - rt - > u . dst . lastuse ) < expire ) {
2007-02-10 03:25:52 +03:00
rtp = & rt - > u . dst . dn_next ;
2005-04-17 02:20:36 +04:00
continue ;
}
2007-02-10 03:25:52 +03:00
* rtp = rt - > u . dst . dn_next ;
rt - > u . dst . dn_next = NULL ;
2005-04-17 02:20:36 +04:00
dnrt_free ( rt ) ;
}
spin_unlock ( & dn_rt_hash_table [ i ] . lock ) ;
if ( ( jiffies - now ) > 0 )
break ;
}
mod_timer ( & dn_route_timer , now + decnet_dst_gc_interval * HZ ) ;
}
static int dn_dst_gc ( void )
{
struct dn_route * rt , * * rtp ;
int i ;
unsigned long now = jiffies ;
unsigned long expire = 10 * HZ ;
for ( i = 0 ; i < = dn_rt_hash_mask ; i + + ) {
spin_lock_bh ( & dn_rt_hash_table [ i ] . lock ) ;
rtp = & dn_rt_hash_table [ i ] . chain ;
while ( ( rt = * rtp ) ! = NULL ) {
if ( atomic_read ( & rt - > u . dst . __refcnt ) | |
( now - rt - > u . dst . lastuse ) < expire ) {
2007-02-10 03:25:52 +03:00
rtp = & rt - > u . dst . dn_next ;
2005-04-17 02:20:36 +04:00
continue ;
}
2007-02-10 03:25:52 +03:00
* rtp = rt - > u . dst . dn_next ;
rt - > u . dst . dn_next = NULL ;
2005-04-17 02:20:36 +04:00
dnrt_drop ( rt ) ;
break ;
}
spin_unlock_bh ( & dn_rt_hash_table [ i ] . lock ) ;
}
return 0 ;
}
/*
* The decnet standards don ' t impose a particular minimum mtu , what they
* do insist on is that the routing layer accepts a datagram of at least
* 230 bytes long . Here we have to subtract the routing header length from
* 230 to get the minimum acceptable mtu . If there is no neighbour , then we
* assume the worst and use a long header size .
*
* We update both the mtu and the advertised mss ( i . e . the segment size we
* advertise to the other end ) .
*/
static void dn_dst_update_pmtu ( struct dst_entry * dst , u32 mtu )
{
u32 min_mtu = 230 ;
struct dn_dev * dn = dst - > neighbour ?
( struct dn_dev * ) dst - > neighbour - > dev - > dn_ptr : NULL ;
if ( dn & & dn - > use_long = = 0 )
min_mtu - = 6 ;
else
min_mtu - = 21 ;
if ( dst - > metrics [ RTAX_MTU - 1 ] > mtu & & mtu > = min_mtu ) {
if ( ! ( dst_metric_locked ( dst , RTAX_MTU ) ) ) {
dst - > metrics [ RTAX_MTU - 1 ] = mtu ;
dst_set_expires ( dst , dn_rt_mtu_expires ) ;
}
if ( ! ( dst_metric_locked ( dst , RTAX_ADVMSS ) ) ) {
u32 mss = mtu - DN_MAX_NSP_DATA_HEADER ;
if ( dst - > metrics [ RTAX_ADVMSS - 1 ] > mss )
dst - > metrics [ RTAX_ADVMSS - 1 ] = mss ;
}
}
}
2007-02-09 17:24:40 +03:00
/*
2005-04-17 02:20:36 +04:00
* When a route has been marked obsolete . ( e . g . routing cache flush )
*/
static struct dst_entry * dn_dst_check ( struct dst_entry * dst , __u32 cookie )
{
return NULL ;
}
static struct dst_entry * dn_dst_negative_advice ( struct dst_entry * dst )
{
dst_release ( dst ) ;
return NULL ;
}
static void dn_dst_link_failure ( struct sk_buff * skb )
{
return ;
}
static inline int compare_keys ( struct flowi * fl1 , struct flowi * fl2 )
{
2006-10-12 11:49:15 +04:00
return ( ( fl1 - > nl_u . dn_u . daddr ^ fl2 - > nl_u . dn_u . daddr ) |
( fl1 - > nl_u . dn_u . saddr ^ fl2 - > nl_u . dn_u . saddr ) |
2006-11-10 02:20:38 +03:00
( fl1 - > mark ^ fl2 - > mark ) |
2006-10-12 11:49:15 +04:00
( fl1 - > nl_u . dn_u . scope ^ fl2 - > nl_u . dn_u . scope ) |
( fl1 - > oif ^ fl2 - > oif ) |
( fl1 - > iif ^ fl2 - > iif ) ) = = 0 ;
2005-04-17 02:20:36 +04:00
}
static int dn_insert_route ( struct dn_route * rt , unsigned hash , struct dn_route * * rp )
{
struct dn_route * rth , * * rthp ;
unsigned long now = jiffies ;
rthp = & dn_rt_hash_table [ hash ] . chain ;
spin_lock_bh ( & dn_rt_hash_table [ hash ] . lock ) ;
while ( ( rth = * rthp ) ! = NULL ) {
if ( compare_keys ( & rth - > fl , & rt - > fl ) ) {
/* Put it first */
2007-02-10 03:25:52 +03:00
* rthp = rth - > u . dst . dn_next ;
rcu_assign_pointer ( rth - > u . dst . dn_next ,
2005-04-17 02:20:36 +04:00
dn_rt_hash_table [ hash ] . chain ) ;
rcu_assign_pointer ( dn_rt_hash_table [ hash ] . chain , rth ) ;
2007-11-11 08:28:34 +03:00
dst_use ( & rth - > u . dst , now ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_bh ( & dn_rt_hash_table [ hash ] . lock ) ;
dnrt_drop ( rt ) ;
* rp = rth ;
return 0 ;
}
2007-02-10 03:25:52 +03:00
rthp = & rth - > u . dst . dn_next ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 03:25:52 +03:00
rcu_assign_pointer ( rt - > u . dst . dn_next , dn_rt_hash_table [ hash ] . chain ) ;
2005-04-17 02:20:36 +04:00
rcu_assign_pointer ( dn_rt_hash_table [ hash ] . chain , rt ) ;
2007-02-09 17:24:40 +03:00
2007-11-11 08:28:34 +03:00
dst_use ( & rt - > u . dst , now ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_bh ( & dn_rt_hash_table [ hash ] . lock ) ;
* rp = rt ;
return 0 ;
}
void dn_run_flush ( unsigned long dummy )
{
int i ;
struct dn_route * rt , * next ;
for ( i = 0 ; i < dn_rt_hash_mask ; i + + ) {
spin_lock_bh ( & dn_rt_hash_table [ i ] . lock ) ;
if ( ( rt = xchg ( & dn_rt_hash_table [ i ] . chain , NULL ) ) = = NULL )
goto nothing_to_declare ;
for ( ; rt ; rt = next ) {
2007-02-10 03:25:52 +03:00
next = rt - > u . dst . dn_next ;
rt - > u . dst . dn_next = NULL ;
2005-04-17 02:20:36 +04:00
dst_free ( ( struct dst_entry * ) rt ) ;
}
nothing_to_declare :
spin_unlock_bh ( & dn_rt_hash_table [ i ] . lock ) ;
}
}
static DEFINE_SPINLOCK ( dn_rt_flush_lock ) ;
void dn_rt_cache_flush ( int delay )
{
unsigned long now = jiffies ;
int user_mode = ! in_interrupt ( ) ;
if ( delay < 0 )
delay = dn_rt_min_delay ;
spin_lock_bh ( & dn_rt_flush_lock ) ;
if ( del_timer ( & dn_rt_flush_timer ) & & delay > 0 & & dn_rt_deadline ) {
long tmo = ( long ) ( dn_rt_deadline - now ) ;
if ( user_mode & & tmo < dn_rt_max_delay - dn_rt_min_delay )
tmo = 0 ;
if ( delay > tmo )
delay = tmo ;
}
if ( delay < = 0 ) {
spin_unlock_bh ( & dn_rt_flush_lock ) ;
dn_run_flush ( 0 ) ;
return ;
}
if ( dn_rt_deadline = = 0 )
dn_rt_deadline = now + dn_rt_max_delay ;
dn_rt_flush_timer . expires = now + delay ;
add_timer ( & dn_rt_flush_timer ) ;
spin_unlock_bh ( & dn_rt_flush_lock ) ;
}
/**
* dn_return_short - Return a short packet to its sender
* @ skb : The packet to return
*
*/
static int dn_return_short ( struct sk_buff * skb )
{
struct dn_skb_cb * cb ;
unsigned char * ptr ;
2006-03-21 09:42:39 +03:00
__le16 * src ;
__le16 * dst ;
__le16 tmp ;
2005-04-17 02:20:36 +04:00
/* Add back headers */
2007-04-11 07:50:43 +04:00
skb_push ( skb , skb - > data - skb_network_header ( skb ) ) ;
2005-04-17 02:20:36 +04:00
if ( ( skb = skb_unshare ( skb , GFP_ATOMIC ) ) = = NULL )
return NET_RX_DROP ;
cb = DN_SKB_CB ( skb ) ;
/* Skip packet length and point to flags */
ptr = skb - > data + 2 ;
* ptr + + = ( cb - > rt_flags & ~ DN_RT_F_RQR ) | DN_RT_F_RTS ;
2006-03-21 09:42:39 +03:00
dst = ( __le16 * ) ptr ;
2005-04-17 02:20:36 +04:00
ptr + = 2 ;
2006-03-21 09:42:39 +03:00
src = ( __le16 * ) ptr ;
2005-04-17 02:20:36 +04:00
ptr + = 2 ;
* ptr = 0 ; /* Zero hop count */
/* Swap source and destination */
tmp = * src ;
* src = * dst ;
* dst = tmp ;
skb - > pkt_type = PACKET_OUTGOING ;
dn_rt_finish_output ( skb , NULL , NULL ) ;
return NET_RX_SUCCESS ;
}
/**
* dn_return_long - Return a long packet to its sender
* @ skb : The long format packet to return
*
*/
static int dn_return_long ( struct sk_buff * skb )
{
struct dn_skb_cb * cb ;
unsigned char * ptr ;
unsigned char * src_addr , * dst_addr ;
unsigned char tmp [ ETH_ALEN ] ;
/* Add back all headers */
2007-04-11 07:50:43 +04:00
skb_push ( skb , skb - > data - skb_network_header ( skb ) ) ;
2005-04-17 02:20:36 +04:00
if ( ( skb = skb_unshare ( skb , GFP_ATOMIC ) ) = = NULL )
return NET_RX_DROP ;
cb = DN_SKB_CB ( skb ) ;
/* Ignore packet length and point to flags */
ptr = skb - > data + 2 ;
/* Skip padding */
if ( * ptr & DN_RT_F_PF ) {
char padlen = ( * ptr & ~ DN_RT_F_PF ) ;
ptr + = padlen ;
}
* ptr + + = ( cb - > rt_flags & ~ DN_RT_F_RQR ) | DN_RT_F_RTS ;
ptr + = 2 ;
dst_addr = ptr ;
ptr + = 8 ;
src_addr = ptr ;
ptr + = 6 ;
* ptr = 0 ; /* Zero hop count */
/* Swap source and destination */
memcpy ( tmp , src_addr , ETH_ALEN ) ;
memcpy ( src_addr , dst_addr , ETH_ALEN ) ;
memcpy ( dst_addr , tmp , ETH_ALEN ) ;
skb - > pkt_type = PACKET_OUTGOING ;
dn_rt_finish_output ( skb , dst_addr , src_addr ) ;
return NET_RX_SUCCESS ;
}
/**
* dn_route_rx_packet - Try and find a route for an incoming packet
* @ skb : The packet to find a route for
*
* Returns : result of input function if route is found , error code otherwise
*/
static int dn_route_rx_packet ( struct sk_buff * skb )
{
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
int err ;
if ( ( err = dn_route_input ( skb ) ) = = 0 )
return dst_input ( skb ) ;
if ( decnet_debug_level & 4 ) {
char * devname = skb - > dev ? skb - > dev - > name : " ??? " ;
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
printk ( KERN_DEBUG
" DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d \n " ,
2006-03-21 09:42:39 +03:00
( int ) cb - > rt_flags , devname , skb - > len ,
dn_ntohs ( cb - > src ) , dn_ntohs ( cb - > dst ) ,
2005-04-17 02:20:36 +04:00
err , skb - > pkt_type ) ;
}
if ( ( skb - > pkt_type = = PACKET_HOST ) & & ( cb - > rt_flags & DN_RT_F_RQR ) ) {
switch ( cb - > rt_flags & DN_RT_PKT_MSK ) {
case DN_RT_PKT_SHORT :
return dn_return_short ( skb ) ;
case DN_RT_PKT_LONG :
return dn_return_long ( skb ) ;
}
}
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
static int dn_route_rx_long ( struct sk_buff * skb )
{
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
unsigned char * ptr = skb - > data ;
if ( ! pskb_may_pull ( skb , 21 ) ) /* 20 for long header, 1 for shortest nsp */
goto drop_it ;
skb_pull ( skb , 20 ) ;
2007-03-13 19:06:52 +03:00
skb_reset_transport_header ( skb ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:40 +03:00
/* Destination info */
ptr + = 2 ;
2006-03-21 09:42:39 +03:00
cb - > dst = dn_eth2dn ( ptr ) ;
2007-02-09 17:24:40 +03:00
if ( memcmp ( ptr , dn_hiord_addr , 4 ) ! = 0 )
goto drop_it ;
ptr + = 6 ;
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:40 +03:00
/* Source info */
ptr + = 2 ;
2006-03-21 09:42:39 +03:00
cb - > src = dn_eth2dn ( ptr ) ;
2007-02-09 17:24:40 +03:00
if ( memcmp ( ptr , dn_hiord_addr , 4 ) ! = 0 )
goto drop_it ;
ptr + = 6 ;
/* Other junk */
ptr + + ;
cb - > hops = * ptr + + ; /* Visit Count */
2005-04-17 02:20:36 +04:00
return NF_HOOK ( PF_DECnet , NF_DN_PRE_ROUTING , skb , skb - > dev , NULL , dn_route_rx_packet ) ;
drop_it :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
static int dn_route_rx_short ( struct sk_buff * skb )
{
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
unsigned char * ptr = skb - > data ;
if ( ! pskb_may_pull ( skb , 6 ) ) /* 5 for short header + 1 for shortest nsp */
goto drop_it ;
skb_pull ( skb , 5 ) ;
2007-03-13 19:06:52 +03:00
skb_reset_transport_header ( skb ) ;
2005-04-17 02:20:36 +04:00
2006-03-21 09:42:39 +03:00
cb - > dst = * ( __le16 * ) ptr ;
2007-02-09 17:24:40 +03:00
ptr + = 2 ;
cb - > src = * ( __le16 * ) ptr ;
ptr + = 2 ;
cb - > hops = * ptr & 0x3f ;
2005-04-17 02:20:36 +04:00
return NF_HOOK ( PF_DECnet , NF_DN_PRE_ROUTING , skb , skb - > dev , NULL , dn_route_rx_packet ) ;
drop_it :
2007-02-09 17:24:40 +03:00
kfree_skb ( skb ) ;
return NET_RX_DROP ;
2005-04-17 02:20:36 +04:00
}
static int dn_route_discard ( struct sk_buff * skb )
{
/*
* I know we drop the packet here , but thats considered success in
* this case
*/
kfree_skb ( skb ) ;
return NET_RX_SUCCESS ;
}
static int dn_route_ptp_hello ( struct sk_buff * skb )
{
dn_dev_hello ( skb ) ;
dn_neigh_pointopoint_hello ( skb ) ;
return NET_RX_SUCCESS ;
}
2005-08-10 06:34:12 +04:00
int dn_route_rcv ( struct sk_buff * skb , struct net_device * dev , struct packet_type * pt , struct net_device * orig_dev )
2005-04-17 02:20:36 +04:00
{
struct dn_skb_cb * cb ;
unsigned char flags = 0 ;
2006-03-21 09:42:39 +03:00
__u16 len = dn_ntohs ( * ( __le16 * ) skb - > data ) ;
2005-04-17 02:20:36 +04:00
struct dn_dev * dn = ( struct dn_dev * ) dev - > dn_ptr ;
unsigned char padlen = 0 ;
2007-09-17 22:53:39 +04:00
if ( dev - > nd_net ! = & init_net )
goto dump_it ;
2005-04-17 02:20:36 +04:00
if ( dn = = NULL )
goto dump_it ;
if ( ( skb = skb_share_check ( skb , GFP_ATOMIC ) ) = = NULL )
goto out ;
if ( ! pskb_may_pull ( skb , 3 ) )
goto dump_it ;
skb_pull ( skb , 2 ) ;
if ( len > skb - > len )
goto dump_it ;
skb_trim ( skb , len ) ;
flags = * skb - > data ;
cb = DN_SKB_CB ( skb ) ;
cb - > stamp = jiffies ;
cb - > iif = dev - > ifindex ;
/*
* If we have padding , remove it .
*/
if ( flags & DN_RT_F_PF ) {
padlen = flags & ~ DN_RT_F_PF ;
if ( ! pskb_may_pull ( skb , padlen + 1 ) )
goto dump_it ;
skb_pull ( skb , padlen ) ;
flags = * skb - > data ;
}
2007-04-11 07:45:18 +04:00
skb_reset_network_header ( skb ) ;
2005-04-17 02:20:36 +04:00
/*
* Weed out future version DECnet
*/
if ( flags & DN_RT_F_VER )
goto dump_it ;
cb - > rt_flags = flags ;
if ( decnet_debug_level & 1 )
2007-02-09 17:24:40 +03:00
printk ( KERN_DEBUG
2005-04-17 02:20:36 +04:00
" dn_route_rcv: got 0x%02x from %s [%d %d %d] \n " ,
2007-02-09 17:24:40 +03:00
( int ) flags , ( dev ) ? dev - > name : " ??? " , len , skb - > len ,
2005-04-17 02:20:36 +04:00
padlen ) ;
2007-02-09 17:24:40 +03:00
if ( flags & DN_RT_PKT_CNTL ) {
2006-06-10 03:10:40 +04:00
if ( unlikely ( skb_linearize ( skb ) ) )
2005-04-17 02:20:36 +04:00
goto dump_it ;
2007-02-09 17:24:40 +03:00
switch ( flags & DN_RT_CNTL_MSK ) {
case DN_RT_PKT_INIT :
2005-04-17 02:20:36 +04:00
dn_dev_init_pkt ( skb ) ;
break ;
2007-02-09 17:24:40 +03:00
case DN_RT_PKT_VERI :
2005-04-17 02:20:36 +04:00
dn_dev_veri_pkt ( skb ) ;
break ;
}
if ( dn - > parms . state ! = DN_DEV_S_RU )
goto dump_it ;
switch ( flags & DN_RT_CNTL_MSK ) {
2007-02-09 17:24:40 +03:00
case DN_RT_PKT_HELO :
2005-04-17 02:20:36 +04:00
return NF_HOOK ( PF_DECnet , NF_DN_HELLO , skb , skb - > dev , NULL , dn_route_ptp_hello ) ;
2007-02-09 17:24:40 +03:00
case DN_RT_PKT_L1RT :
case DN_RT_PKT_L2RT :
return NF_HOOK ( PF_DECnet , NF_DN_ROUTE , skb , skb - > dev , NULL , dn_route_discard ) ;
case DN_RT_PKT_ERTH :
2005-04-17 02:20:36 +04:00
return NF_HOOK ( PF_DECnet , NF_DN_HELLO , skb , skb - > dev , NULL , dn_neigh_router_hello ) ;
2007-02-09 17:24:40 +03:00
case DN_RT_PKT_EEDH :
2005-04-17 02:20:36 +04:00
return NF_HOOK ( PF_DECnet , NF_DN_HELLO , skb , skb - > dev , NULL , dn_neigh_endnode_hello ) ;
2007-02-09 17:24:40 +03:00
}
} else {
2005-04-17 02:20:36 +04:00
if ( dn - > parms . state ! = DN_DEV_S_RU )
goto dump_it ;
skb_pull ( skb , 1 ) ; /* Pull flags */
2007-02-09 17:24:40 +03:00
switch ( flags & DN_RT_PKT_MSK ) {
case DN_RT_PKT_LONG :
return dn_route_rx_long ( skb ) ;
case DN_RT_PKT_SHORT :
return dn_route_rx_short ( skb ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 17:24:40 +03:00
}
2005-04-17 02:20:36 +04:00
dump_it :
kfree_skb ( skb ) ;
out :
return NET_RX_DROP ;
}
static int dn_output ( struct sk_buff * skb )
{
struct dst_entry * dst = skb - > dst ;
struct dn_route * rt = ( struct dn_route * ) dst ;
struct net_device * dev = dst - > dev ;
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
struct neighbour * neigh ;
int err = - EINVAL ;
if ( ( neigh = dst - > neighbour ) = = NULL )
goto error ;
skb - > dev = dev ;
cb - > src = rt - > rt_saddr ;
cb - > dst = rt - > rt_daddr ;
/*
* Always set the Intra - Ethernet bit on all outgoing packets
* originated on this node . Only valid flag from upper layers
* is return - to - sender - requested . Set hop count to 0 too .
*/
cb - > rt_flags & = ~ DN_RT_F_RQR ;
cb - > rt_flags | = DN_RT_F_IE ;
cb - > hops = 0 ;
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 " ) ;
kfree_skb ( skb ) ;
return err ;
}
static int dn_forward ( struct sk_buff * skb )
{
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
struct dst_entry * dst = skb - > dst ;
struct dn_dev * dn_db = dst - > dev - > dn_ptr ;
struct dn_route * rt ;
struct neighbour * neigh = dst - > neighbour ;
int header_len ;
# ifdef CONFIG_NETFILTER
struct net_device * dev = skb - > dev ;
# endif
if ( skb - > pkt_type ! = PACKET_HOST )
goto drop ;
/* Ensure that we have enough space for headers */
rt = ( struct dn_route * ) skb - > dst ;
header_len = dn_db - > use_long ? 21 : 6 ;
if ( skb_cow ( skb , LL_RESERVED_SPACE ( rt - > u . dst . dev ) + header_len ) )
goto drop ;
/*
* Hop count exceeded .
*/
if ( + + cb - > hops > 30 )
goto drop ;
skb - > dev = rt - > u . dst . dev ;
/*
* If packet goes out same interface it came in on , then set
* the Intra - Ethernet bit . This has no effect for short
* packets , so we don ' t need to test for them here .
*/
cb - > rt_flags & = ~ DN_RT_F_IE ;
if ( rt - > rt_flags & RTCF_DOREDIRECT )
cb - > rt_flags | = DN_RT_F_IE ;
return NF_HOOK ( PF_DECnet , NF_DN_FORWARD , skb , dev , skb - > dev , neigh - > output ) ;
drop :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
/*
* Used to catch bugs . This should never normally get
* called .
*/
static int dn_rt_bug ( struct sk_buff * skb )
{
if ( net_ratelimit ( ) ) {
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
printk ( KERN_DEBUG " dn_rt_bug: skb from:%04x to:%04x \n " ,
2006-03-21 09:42:39 +03:00
dn_ntohs ( cb - > src ) , dn_ntohs ( cb - > dst ) ) ;
2005-04-17 02:20:36 +04:00
}
kfree_skb ( skb ) ;
return NET_RX_BAD ;
}
static int dn_rt_set_next_hop ( struct dn_route * rt , struct dn_fib_res * res )
{
struct dn_fib_info * fi = res - > fi ;
struct net_device * dev = rt - > u . dst . dev ;
struct neighbour * n ;
unsigned mss ;
if ( fi ) {
if ( DN_FIB_RES_GW ( * res ) & &
DN_FIB_RES_NH ( * res ) . nh_scope = = RT_SCOPE_LINK )
rt - > rt_gateway = DN_FIB_RES_GW ( * res ) ;
memcpy ( rt - > u . dst . metrics , fi - > fib_metrics ,
sizeof ( rt - > u . dst . metrics ) ) ;
}
rt - > rt_type = res - > type ;
if ( dev ! = NULL & & rt - > u . dst . neighbour = = NULL ) {
n = __neigh_lookup_errno ( & dn_neigh_table , & rt - > rt_gateway , dev ) ;
if ( IS_ERR ( n ) )
return PTR_ERR ( n ) ;
rt - > u . dst . neighbour = n ;
}
2007-02-09 17:24:40 +03:00
if ( rt - > u . dst . metrics [ RTAX_MTU - 1 ] = = 0 | |
rt - > u . dst . metrics [ RTAX_MTU - 1 ] > rt - > u . dst . dev - > mtu )
2005-04-17 02:20:36 +04:00
rt - > u . dst . metrics [ RTAX_MTU - 1 ] = rt - > u . dst . dev - > mtu ;
mss = dn_mss_from_pmtu ( dev , dst_mtu ( & rt - > u . dst ) ) ;
if ( rt - > u . dst . metrics [ RTAX_ADVMSS - 1 ] = = 0 | |
rt - > u . dst . metrics [ RTAX_ADVMSS - 1 ] > mss )
rt - > u . dst . metrics [ RTAX_ADVMSS - 1 ] = mss ;
return 0 ;
}
2006-03-21 09:42:39 +03:00
static inline int dn_match_addr ( __le16 addr1 , __le16 addr2 )
2005-04-17 02:20:36 +04:00
{
__u16 tmp = dn_ntohs ( addr1 ) ^ dn_ntohs ( addr2 ) ;
int match = 16 ;
while ( tmp ) {
tmp > > = 1 ;
match - - ;
}
return match ;
}
2006-03-21 09:42:39 +03:00
static __le16 dnet_select_source ( const struct net_device * dev , __le16 daddr , int scope )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:42:39 +03:00
__le16 saddr = 0 ;
2005-04-17 02:20:36 +04:00
struct dn_dev * dn_db = dev - > dn_ptr ;
struct dn_ifaddr * ifa ;
int best_match = 0 ;
int ret ;
read_lock ( & dev_base_lock ) ;
for ( ifa = dn_db - > ifa_list ; ifa ; ifa = ifa - > ifa_next ) {
if ( ifa - > ifa_scope > scope )
continue ;
if ( ! daddr ) {
saddr = ifa - > ifa_local ;
break ;
}
ret = dn_match_addr ( daddr , ifa - > ifa_local ) ;
if ( ret > best_match )
saddr = ifa - > ifa_local ;
if ( best_match = = 0 )
saddr = ifa - > ifa_local ;
}
read_unlock ( & dev_base_lock ) ;
return saddr ;
}
2006-03-21 09:42:39 +03:00
static inline __le16 __dn_fib_res_prefsrc ( struct dn_fib_res * res )
2005-04-17 02:20:36 +04:00
{
return dnet_select_source ( DN_FIB_RES_DEV ( * res ) , DN_FIB_RES_GW ( * res ) , res - > scope ) ;
}
2006-03-21 09:42:39 +03:00
static inline __le16 dn_fib_rules_map_destination ( __le16 daddr , struct dn_fib_res * res )
2005-04-17 02:20:36 +04:00
{
2006-03-21 09:42:39 +03:00
__le16 mask = dnet_make_mask ( res - > prefixlen ) ;
2005-04-17 02:20:36 +04:00
return ( daddr & ~ mask ) | res - > fi - > fib_nh - > nh_gw ;
}
static int dn_route_output_slow ( struct dst_entry * * pprt , const struct flowi * oldflp , int try_hard )
{
2007-02-09 17:24:40 +03:00
struct flowi fl = { . nl_u = { . dn_u =
2005-04-17 02:20:36 +04:00
{ . daddr = oldflp - > fld_dst ,
. saddr = oldflp - > fld_src ,
. scope = RT_SCOPE_UNIVERSE ,
} } ,
2006-11-10 02:20:38 +03:00
. mark = oldflp - > mark ,
2007-09-27 09:10:56 +04:00
. iif = init_net . loopback_dev - > ifindex ,
2005-04-17 02:20:36 +04:00
. oif = oldflp - > oif } ;
struct dn_route * rt = NULL ;
2007-05-04 02:13:45 +04:00
struct net_device * dev_out = NULL , * dev ;
2005-04-17 02:20:36 +04:00
struct neighbour * neigh = NULL ;
unsigned hash ;
unsigned flags = 0 ;
struct dn_fib_res res = { . fi = NULL , . type = RTN_UNICAST } ;
int err ;
int free_res = 0 ;
2006-03-21 09:42:39 +03:00
__le16 gateway = 0 ;
2005-04-17 02:20:36 +04:00
if ( decnet_debug_level & 16 )
printk ( KERN_DEBUG
" dn_route_output_slow: dst=%04x src=%04x mark=%d "
2006-03-21 09:42:39 +03:00
" iif=%d oif=%d \n " , dn_ntohs ( oldflp - > fld_dst ) ,
dn_ntohs ( oldflp - > fld_src ) ,
2007-09-27 09:10:56 +04:00
oldflp - > mark , init_net . loopback_dev - > ifindex , oldflp - > oif ) ;
2005-04-17 02:20:36 +04:00
/* If we have an output interface, verify its a DECnet device */
if ( oldflp - > oif ) {
2007-09-17 22:56:21 +04:00
dev_out = dev_get_by_index ( & init_net , oldflp - > oif ) ;
2005-04-17 02:20:36 +04:00
err = - ENODEV ;
if ( dev_out & & dev_out - > dn_ptr = = NULL ) {
dev_put ( dev_out ) ;
dev_out = NULL ;
}
if ( dev_out = = NULL )
goto out ;
}
/* If we have a source address, verify that its a local address */
if ( oldflp - > fld_src ) {
err = - EADDRNOTAVAIL ;
if ( dev_out ) {
if ( dn_dev_islocal ( dev_out , oldflp - > fld_src ) )
goto source_ok ;
dev_put ( dev_out ) ;
goto out ;
}
read_lock ( & dev_base_lock ) ;
2007-09-17 22:56:21 +04:00
for_each_netdev ( & init_net , dev ) {
2007-05-04 02:13:45 +04:00
if ( ! dev - > dn_ptr )
2005-04-17 02:20:36 +04:00
continue ;
2007-05-04 02:13:45 +04:00
if ( ! dn_dev_islocal ( dev , oldflp - > fld_src ) )
2006-08-03 01:14:44 +04:00
continue ;
2007-05-04 02:13:45 +04:00
if ( ( dev - > flags & IFF_LOOPBACK ) & &
2006-08-03 01:14:44 +04:00
oldflp - > fld_dst & &
2007-05-04 02:13:45 +04:00
! dn_dev_islocal ( dev , oldflp - > fld_dst ) )
2006-08-03 01:14:44 +04:00
continue ;
2007-05-04 02:13:45 +04:00
dev_out = dev ;
2006-08-03 01:14:44 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
read_unlock ( & dev_base_lock ) ;
if ( dev_out = = NULL )
goto out ;
dev_hold ( dev_out ) ;
source_ok :
;
}
/* No destination? Assume its local */
if ( ! fl . fld_dst ) {
fl . fld_dst = fl . fld_src ;
err = - EADDRNOTAVAIL ;
if ( dev_out )
dev_put ( dev_out ) ;
2007-09-27 09:10:56 +04:00
dev_out = init_net . loopback_dev ;
2005-04-17 02:20:36 +04:00
dev_hold ( dev_out ) ;
if ( ! fl . fld_dst ) {
fl . fld_dst =
fl . fld_src = dnet_select_source ( dev_out , 0 ,
RT_SCOPE_HOST ) ;
if ( ! fl . fld_dst )
goto out ;
}
2007-09-27 09:10:56 +04:00
fl . oif = init_net . loopback_dev - > ifindex ;
2005-04-17 02:20:36 +04:00
res . type = RTN_LOCAL ;
goto make_route ;
}
if ( decnet_debug_level & 16 )
printk ( KERN_DEBUG
" dn_route_output_slow: initial checks complete. "
2006-03-21 09:42:39 +03:00
" dst=%o4x src=%04x oif=%d try_hard=%d \n " ,
dn_ntohs ( fl . fld_dst ) , dn_ntohs ( fl . fld_src ) ,
fl . oif , try_hard ) ;
2005-04-17 02:20:36 +04:00
/*
* N . B . If the kernel is compiled without router support then
* dn_fib_lookup ( ) will evaluate to non - zero so this if ( ) block
* will always be executed .
*/
err = - ESRCH ;
if ( try_hard | | ( err = dn_fib_lookup ( & fl , & res ) ) ! = 0 ) {
struct dn_dev * dn_db ;
if ( err ! = - ESRCH )
goto out ;
/*
2007-02-09 17:24:40 +03:00
* Here the fallback is basically the standard algorithm for
2005-04-17 02:20:36 +04:00
* routing in endnodes which is described in the DECnet routing
* docs
*
* If we are not trying hard , look in neighbour cache .
* The result is tested to ensure that if a specific output
2007-02-09 17:24:40 +03:00
* device / source address was requested , then we honour that
2005-04-17 02:20:36 +04:00
* here
*/
if ( ! try_hard ) {
neigh = neigh_lookup_nodev ( & dn_neigh_table , & fl . fld_dst ) ;
if ( neigh ) {
2007-02-09 17:24:40 +03:00
if ( ( oldflp - > oif & &
2005-04-17 02:20:36 +04:00
( neigh - > dev - > ifindex ! = oldflp - > oif ) ) | |
( oldflp - > fld_src & &
( ! dn_dev_islocal ( neigh - > dev ,
oldflp - > fld_src ) ) ) ) {
neigh_release ( neigh ) ;
neigh = NULL ;
} else {
if ( dev_out )
dev_put ( dev_out ) ;
if ( dn_dev_islocal ( neigh - > dev , fl . fld_dst ) ) {
2007-09-27 09:10:56 +04:00
dev_out = init_net . loopback_dev ;
2005-04-17 02:20:36 +04:00
res . type = RTN_LOCAL ;
} else {
dev_out = neigh - > dev ;
}
dev_hold ( dev_out ) ;
goto select_source ;
}
}
}
/* Not there? Perhaps its a local address */
if ( dev_out = = NULL )
dev_out = dn_dev_get_default ( ) ;
err = - ENODEV ;
if ( dev_out = = NULL )
goto out ;
dn_db = dev_out - > dn_ptr ;
/* Possible improvement - check all devices for local addr */
if ( dn_dev_islocal ( dev_out , fl . fld_dst ) ) {
dev_put ( dev_out ) ;
2007-09-27 09:10:56 +04:00
dev_out = init_net . loopback_dev ;
2005-04-17 02:20:36 +04:00
dev_hold ( dev_out ) ;
res . type = RTN_LOCAL ;
goto select_source ;
}
/* Not local either.... try sending it to the default router */
neigh = neigh_clone ( dn_db - > router ) ;
BUG_ON ( neigh & & neigh - > dev ! = dev_out ) ;
/* Ok then, we assume its directly connected and move on */
select_source :
if ( neigh )
gateway = ( ( struct dn_neigh * ) neigh ) - > addr ;
if ( gateway = = 0 )
gateway = fl . fld_dst ;
if ( fl . fld_src = = 0 ) {
fl . fld_src = dnet_select_source ( dev_out , gateway ,
res . type = = RTN_LOCAL ?
2007-02-09 17:24:40 +03:00
RT_SCOPE_HOST :
2005-04-17 02:20:36 +04:00
RT_SCOPE_LINK ) ;
if ( fl . fld_src = = 0 & & res . type ! = RTN_LOCAL )
goto e_addr ;
}
fl . oif = dev_out - > ifindex ;
goto make_route ;
}
free_res = 1 ;
if ( res . type = = RTN_NAT )
goto e_inval ;
if ( res . type = = RTN_LOCAL ) {
if ( ! fl . fld_src )
fl . fld_src = fl . fld_dst ;
if ( dev_out )
dev_put ( dev_out ) ;
2007-09-27 09:10:56 +04:00
dev_out = init_net . loopback_dev ;
2005-04-17 02:20:36 +04:00
dev_hold ( dev_out ) ;
fl . oif = dev_out - > ifindex ;
if ( res . fi )
dn_fib_info_put ( res . fi ) ;
res . fi = NULL ;
goto make_route ;
}
if ( res . fi - > fib_nhs > 1 & & fl . oif = = 0 )
dn_fib_select_multipath ( & fl , & res ) ;
2007-02-09 17:24:40 +03:00
/*
2005-04-17 02:20:36 +04:00
* We could add some logic to deal with default routes here and
* get rid of some of the special casing above .
*/
if ( ! fl . fld_src )
fl . fld_src = DN_FIB_RES_PREFSRC ( res ) ;
2007-02-09 17:24:40 +03:00
2005-04-17 02:20:36 +04:00
if ( dev_out )
dev_put ( dev_out ) ;
dev_out = DN_FIB_RES_DEV ( res ) ;
dev_hold ( dev_out ) ;
fl . oif = dev_out - > ifindex ;
gateway = DN_FIB_RES_GW ( res ) ;
make_route :
if ( dev_out - > flags & IFF_LOOPBACK )
flags | = RTCF_LOCAL ;
rt = dst_alloc ( & dn_dst_ops ) ;
if ( rt = = NULL )
goto e_nobufs ;
atomic_set ( & rt - > u . dst . __refcnt , 1 ) ;
rt - > u . dst . flags = DST_HOST ;
rt - > fl . fld_src = oldflp - > fld_src ;
rt - > fl . fld_dst = oldflp - > fld_dst ;
rt - > fl . oif = oldflp - > oif ;
rt - > fl . iif = 0 ;
2006-11-10 02:20:38 +03:00
rt - > fl . mark = oldflp - > mark ;
2005-04-17 02:20:36 +04:00
rt - > rt_saddr = fl . fld_src ;
rt - > rt_daddr = fl . fld_dst ;
rt - > rt_gateway = gateway ? gateway : fl . fld_dst ;
rt - > rt_local_src = fl . fld_src ;
rt - > rt_dst_map = fl . fld_dst ;
rt - > rt_src_map = fl . fld_src ;
rt - > u . dst . dev = dev_out ;
dev_hold ( dev_out ) ;
rt - > u . dst . neighbour = neigh ;
neigh = NULL ;
rt - > u . dst . lastuse = jiffies ;
rt - > u . dst . output = dn_output ;
rt - > u . dst . input = dn_rt_bug ;
rt - > rt_flags = flags ;
if ( flags & RTCF_LOCAL )
rt - > u . dst . input = dn_nsp_rx ;
err = dn_rt_set_next_hop ( rt , & res ) ;
if ( err )
goto e_neighbour ;
hash = dn_hash ( rt - > fl . fld_src , rt - > fl . fld_dst ) ;
dn_insert_route ( rt , hash , ( struct dn_route * * ) pprt ) ;
done :
if ( neigh )
neigh_release ( neigh ) ;
if ( free_res )
dn_fib_res_put ( & res ) ;
if ( dev_out )
dev_put ( dev_out ) ;
out :
return err ;
e_addr :
2007-02-09 17:24:40 +03:00
err = - EADDRNOTAVAIL ;
goto done ;
2005-04-17 02:20:36 +04:00
e_inval :
err = - EINVAL ;
goto done ;
e_nobufs :
err = - ENOBUFS ;
goto done ;
e_neighbour :
dst_free ( & rt - > u . dst ) ;
goto e_nobufs ;
}
/*
* N . B . The flags may be moved into the flowi at some future stage .
*/
static int __dn_route_output_key ( struct dst_entry * * pprt , const struct flowi * flp , int flags )
{
unsigned hash = dn_hash ( flp - > fld_src , flp - > fld_dst ) ;
struct dn_route * rt = NULL ;
if ( ! ( flags & MSG_TRYHARD ) ) {
rcu_read_lock_bh ( ) ;
for ( rt = rcu_dereference ( dn_rt_hash_table [ hash ] . chain ) ; rt ;
2007-02-10 03:25:52 +03:00
rt = rcu_dereference ( rt - > u . dst . dn_next ) ) {
2005-04-17 02:20:36 +04:00
if ( ( flp - > fld_dst = = rt - > fl . fld_dst ) & &
( flp - > fld_src = = rt - > fl . fld_src ) & &
2006-11-10 02:20:38 +03:00
( flp - > mark = = rt - > fl . mark ) & &
2005-04-17 02:20:36 +04:00
( rt - > fl . iif = = 0 ) & &
( rt - > fl . oif = = flp - > oif ) ) {
2007-11-11 08:28:34 +03:00
dst_use ( & rt - > u . dst , jiffies ) ;
2005-04-17 02:20:36 +04:00
rcu_read_unlock_bh ( ) ;
* pprt = & rt - > u . dst ;
return 0 ;
}
}
rcu_read_unlock_bh ( ) ;
}
return dn_route_output_slow ( pprt , flp , flags ) ;
}
static int dn_route_output_key ( struct dst_entry * * pprt , struct flowi * flp , int flags )
{
int err ;
err = __dn_route_output_key ( pprt , flp , flags ) ;
if ( err = = 0 & & flp - > proto ) {
err = xfrm_lookup ( pprt , flp , NULL , 0 ) ;
}
return err ;
}
int dn_route_output_sock ( struct dst_entry * * pprt , struct flowi * fl , struct sock * sk , int flags )
{
int err ;
err = __dn_route_output_key ( pprt , fl , flags & MSG_TRYHARD ) ;
if ( err = = 0 & & fl - > proto ) {
err = xfrm_lookup ( pprt , fl , sk , ! ( flags & MSG_DONTWAIT ) ) ;
}
return err ;
}
static int dn_route_input_slow ( struct sk_buff * skb )
{
struct dn_route * rt = NULL ;
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
struct net_device * in_dev = skb - > dev ;
struct net_device * out_dev = NULL ;
struct dn_dev * dn_db ;
struct neighbour * neigh = NULL ;
unsigned hash ;
int flags = 0 ;
2006-03-21 09:42:39 +03:00
__le16 gateway = 0 ;
__le16 local_src = 0 ;
2007-02-09 17:24:40 +03:00
struct flowi fl = { . nl_u = { . dn_u =
2005-04-17 02:20:36 +04:00
{ . daddr = cb - > dst ,
. saddr = cb - > src ,
. scope = RT_SCOPE_UNIVERSE ,
} } ,
2006-11-10 02:20:38 +03:00
. mark = skb - > mark ,
2005-04-17 02:20:36 +04:00
. iif = skb - > dev - > ifindex } ;
struct dn_fib_res res = { . fi = NULL , . type = RTN_UNREACHABLE } ;
int err = - EINVAL ;
int free_res = 0 ;
dev_hold ( in_dev ) ;
if ( ( dn_db = in_dev - > dn_ptr ) = = NULL )
goto out ;
/* Zero source addresses are not allowed */
if ( fl . fld_src = = 0 )
goto out ;
/*
* In this case we ' ve just received a packet from a source
* outside ourselves pretending to come from us . We don ' t
* allow it any further to prevent routing loops , spoofing and
* other nasties . Loopback packets already have the dst attached
* so this only affects packets which have originated elsewhere .
*/
err = - ENOTUNIQ ;
if ( dn_dev_islocal ( in_dev , cb - > src ) )
goto out ;
err = dn_fib_lookup ( & fl , & res ) ;
if ( err ) {
if ( err ! = - ESRCH )
goto out ;
/*
* Is the destination us ?
*/
if ( ! dn_dev_islocal ( in_dev , cb - > dst ) )
goto e_inval ;
res . type = RTN_LOCAL ;
} else {
2006-03-21 09:42:39 +03:00
__le16 src_map = fl . fld_src ;
2005-04-17 02:20:36 +04:00
free_res = 1 ;
out_dev = DN_FIB_RES_DEV ( res ) ;
if ( out_dev = = NULL ) {
if ( net_ratelimit ( ) )
printk ( KERN_CRIT " Bug in dn_route_input_slow() "
" No output device \n " ) ;
goto e_inval ;
}
dev_hold ( out_dev ) ;
if ( res . r )
2006-08-10 02:56:46 +04:00
src_map = fl . fld_src ; /* no NAT support for now */
2005-04-17 02:20:36 +04:00
gateway = DN_FIB_RES_GW ( res ) ;
if ( res . type = = RTN_NAT ) {
fl . fld_dst = dn_fib_rules_map_destination ( fl . fld_dst , & res ) ;
dn_fib_res_put ( & res ) ;
free_res = 0 ;
if ( dn_fib_lookup ( & fl , & res ) )
goto e_inval ;
free_res = 1 ;
if ( res . type ! = RTN_UNICAST )
goto e_inval ;
flags | = RTCF_DNAT ;
gateway = fl . fld_dst ;
}
fl . fld_src = src_map ;
}
switch ( res . type ) {
case RTN_UNICAST :
/*
* Forwarding check here , we only check for forwarding
* being turned off , if you want to only forward intra
* area , its up to you to set the routing tables up
* correctly .
*/
if ( dn_db - > parms . forwarding = = 0 )
goto e_inval ;
if ( res . fi - > fib_nhs > 1 & & fl . oif = = 0 )
dn_fib_select_multipath ( & fl , & res ) ;
2007-02-09 17:24:40 +03:00
/*
2005-04-17 02:20:36 +04:00
* Check for out_dev = = in_dev . We use the RTCF_DOREDIRECT
* flag as a hint to set the intra - ethernet bit when
* forwarding . If we ' ve got NAT in operation , we don ' t do
* this optimisation .
*/
if ( out_dev = = in_dev & & ! ( flags & RTCF_NAT ) )
flags | = RTCF_DOREDIRECT ;
local_src = DN_FIB_RES_PREFSRC ( res ) ;
case RTN_BLACKHOLE :
case RTN_UNREACHABLE :
break ;
case RTN_LOCAL :
flags | = RTCF_LOCAL ;
fl . fld_src = cb - > dst ;
fl . fld_dst = cb - > src ;
/* Routing tables gave us a gateway */
if ( gateway )
goto make_route ;
/* Packet was intra-ethernet, so we know its on-link */
2006-10-19 07:45:22 +04:00
if ( cb - > rt_flags & DN_RT_F_IE ) {
2005-04-17 02:20:36 +04:00
gateway = cb - > src ;
flags | = RTCF_DIRECTSRC ;
goto make_route ;
}
/* Use the default router if there is one */
neigh = neigh_clone ( dn_db - > router ) ;
if ( neigh ) {
gateway = ( ( struct dn_neigh * ) neigh ) - > addr ;
goto make_route ;
}
/* Close eyes and pray */
gateway = cb - > src ;
flags | = RTCF_DIRECTSRC ;
goto make_route ;
default :
goto e_inval ;
}
make_route :
rt = dst_alloc ( & dn_dst_ops ) ;
if ( rt = = NULL )
goto e_nobufs ;
rt - > rt_saddr = fl . fld_src ;
rt - > rt_daddr = fl . fld_dst ;
rt - > rt_gateway = fl . fld_dst ;
if ( gateway )
rt - > rt_gateway = gateway ;
rt - > rt_local_src = local_src ? local_src : rt - > rt_saddr ;
rt - > rt_dst_map = fl . fld_dst ;
rt - > rt_src_map = fl . fld_src ;
rt - > fl . fld_src = cb - > src ;
rt - > fl . fld_dst = cb - > dst ;
rt - > fl . oif = 0 ;
rt - > fl . iif = in_dev - > ifindex ;
2006-11-10 02:20:38 +03:00
rt - > fl . mark = fl . mark ;
2005-04-17 02:20:36 +04:00
rt - > u . dst . flags = DST_HOST ;
rt - > u . dst . neighbour = neigh ;
rt - > u . dst . dev = out_dev ;
rt - > u . dst . lastuse = jiffies ;
rt - > u . dst . output = dn_rt_bug ;
switch ( res . type ) {
case RTN_UNICAST :
rt - > u . dst . input = dn_forward ;
break ;
case RTN_LOCAL :
rt - > u . dst . output = dn_output ;
rt - > u . dst . input = dn_nsp_rx ;
rt - > u . dst . dev = in_dev ;
flags | = RTCF_LOCAL ;
break ;
default :
case RTN_UNREACHABLE :
case RTN_BLACKHOLE :
2007-11-14 08:34:06 +03:00
rt - > u . dst . input = dst_discard ;
2005-04-17 02:20:36 +04:00
}
rt - > rt_flags = flags ;
if ( rt - > u . dst . dev )
dev_hold ( rt - > u . dst . dev ) ;
err = dn_rt_set_next_hop ( rt , & res ) ;
if ( err )
goto e_neighbour ;
hash = dn_hash ( rt - > fl . fld_src , rt - > fl . fld_dst ) ;
dn_insert_route ( rt , hash , ( struct dn_route * * ) & skb - > dst ) ;
done :
if ( neigh )
neigh_release ( neigh ) ;
if ( free_res )
dn_fib_res_put ( & res ) ;
dev_put ( in_dev ) ;
if ( out_dev )
dev_put ( out_dev ) ;
out :
return err ;
e_inval :
err = - EINVAL ;
goto done ;
e_nobufs :
err = - ENOBUFS ;
goto done ;
e_neighbour :
dst_free ( & rt - > u . dst ) ;
goto done ;
}
int dn_route_input ( struct sk_buff * skb )
{
struct dn_route * rt ;
struct dn_skb_cb * cb = DN_SKB_CB ( skb ) ;
unsigned hash = dn_hash ( cb - > src , cb - > dst ) ;
if ( skb - > dst )
return 0 ;
rcu_read_lock ( ) ;
for ( rt = rcu_dereference ( dn_rt_hash_table [ hash ] . chain ) ; rt ! = NULL ;
2007-02-10 03:25:52 +03:00
rt = rcu_dereference ( rt - > u . dst . dn_next ) ) {
2005-04-17 02:20:36 +04:00
if ( ( rt - > fl . fld_src = = cb - > src ) & &
2007-02-09 17:24:40 +03:00
( rt - > fl . fld_dst = = cb - > dst ) & &
2005-04-17 02:20:36 +04:00
( rt - > fl . oif = = 0 ) & &
2006-11-10 02:20:38 +03:00
( rt - > fl . mark = = skb - > mark ) & &
2005-04-17 02:20:36 +04:00
( rt - > fl . iif = = cb - > iif ) ) {
2007-11-11 08:28:34 +03:00
dst_use ( & rt - > u . dst , jiffies ) ;
2005-04-17 02:20:36 +04:00
rcu_read_unlock ( ) ;
skb - > dst = ( struct dst_entry * ) rt ;
return 0 ;
}
}
rcu_read_unlock ( ) ;
return dn_route_input_slow ( skb ) ;
}
2005-06-19 09:54:12 +04:00
static int dn_rt_fill_info ( struct sk_buff * skb , u32 pid , u32 seq ,
int event , int nowait , unsigned int flags )
2005-04-17 02:20:36 +04:00
{
struct dn_route * rt = ( struct dn_route * ) skb - > dst ;
struct rtmsg * r ;
struct nlmsghdr * nlh ;
2007-04-20 07:29:13 +04:00
unsigned char * b = skb_tail_pointer ( skb ) ;
2006-11-27 20:27:07 +03:00
long expires ;
2005-04-17 02:20:36 +04:00
2005-06-19 09:54:12 +04:00
nlh = NLMSG_NEW ( skb , pid , seq , event , sizeof ( * r ) , flags ) ;
2005-04-17 02:20:36 +04:00
r = NLMSG_DATA ( nlh ) ;
r - > rtm_family = AF_DECnet ;
r - > rtm_dst_len = 16 ;
r - > rtm_src_len = 0 ;
r - > rtm_tos = 0 ;
r - > rtm_table = RT_TABLE_MAIN ;
2006-08-11 10:09:48 +04:00
RTA_PUT_U32 ( skb , RTA_TABLE , RT_TABLE_MAIN ) ;
2005-04-17 02:20:36 +04:00
r - > rtm_type = rt - > rt_type ;
r - > rtm_flags = ( rt - > rt_flags & ~ 0xFFFF ) | RTM_F_CLONED ;
r - > rtm_scope = RT_SCOPE_UNIVERSE ;
r - > rtm_protocol = RTPROT_UNSPEC ;
if ( rt - > rt_flags & RTCF_NOTIFY )
r - > rtm_flags | = RTM_F_NOTIFY ;
RTA_PUT ( skb , RTA_DST , 2 , & rt - > rt_daddr ) ;
if ( rt - > fl . fld_src ) {
r - > rtm_src_len = 16 ;
RTA_PUT ( skb , RTA_SRC , 2 , & rt - > fl . fld_src ) ;
}
if ( rt - > u . dst . dev )
RTA_PUT ( skb , RTA_OIF , sizeof ( int ) , & rt - > u . dst . dev - > ifindex ) ;
/*
* Note to self - change this if input routes reverse direction when
* they deal only with inputs and not with replies like they do
* currently .
*/
RTA_PUT ( skb , RTA_PREFSRC , 2 , & rt - > rt_local_src ) ;
if ( rt - > rt_daddr ! = rt - > rt_gateway )
RTA_PUT ( skb , RTA_GATEWAY , 2 , & rt - > rt_gateway ) ;
if ( rtnetlink_put_metrics ( skb , rt - > u . dst . metrics ) < 0 )
goto rtattr_failure ;
2006-11-27 20:27:07 +03:00
expires = rt - > u . dst . expires ? rt - > u . dst . expires - jiffies : 0 ;
if ( rtnl_put_cacheinfo ( skb , & rt - > u . dst , 0 , 0 , 0 , expires ,
rt - > u . dst . error ) < 0 )
goto rtattr_failure ;
2005-04-17 02:20:36 +04:00
if ( rt - > fl . iif )
RTA_PUT ( skb , RTA_IIF , sizeof ( int ) , & rt - > fl . iif ) ;
2007-04-20 07:29:13 +04:00
nlh - > nlmsg_len = skb_tail_pointer ( skb ) - b ;
2005-04-17 02:20:36 +04:00
return skb - > len ;
nlmsg_failure :
rtattr_failure :
2007-03-26 10:06:12 +04:00
nlmsg_trim ( skb , b ) ;
2007-02-09 17:24:40 +03:00
return - 1 ;
2005-04-17 02:20:36 +04:00
}
/*
* This is called by both endnodes and routers now .
*/
2007-03-22 21:57:46 +03:00
static int dn_cache_getroute ( struct sk_buff * in_skb , struct nlmsghdr * nlh , void * arg )
2005-04-17 02:20:36 +04:00
{
2007-11-30 16:21:31 +03:00
struct net * net = in_skb - > sk - > sk_net ;
2005-04-17 02:20:36 +04:00
struct rtattr * * rta = arg ;
struct rtmsg * rtm = NLMSG_DATA ( nlh ) ;
struct dn_route * rt = NULL ;
struct dn_skb_cb * cb ;
int err ;
struct sk_buff * skb ;
struct flowi fl ;
2007-11-30 16:21:31 +03:00
if ( net ! = & init_net )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
memset ( & fl , 0 , sizeof ( fl ) ) ;
fl . proto = DNPROTO_NSP ;
skb = alloc_skb ( NLMSG_GOODSIZE , GFP_KERNEL ) ;
if ( skb = = NULL )
return - ENOBUFS ;
2007-03-20 01:30:44 +03:00
skb_reset_mac_header ( skb ) ;
2005-04-17 02:20:36 +04:00
cb = DN_SKB_CB ( skb ) ;
if ( rta [ RTA_SRC - 1 ] )
memcpy ( & fl . fld_src , RTA_DATA ( rta [ RTA_SRC - 1 ] ) , 2 ) ;
if ( rta [ RTA_DST - 1 ] )
memcpy ( & fl . fld_dst , RTA_DATA ( rta [ RTA_DST - 1 ] ) , 2 ) ;
if ( rta [ RTA_IIF - 1 ] )
memcpy ( & fl . iif , RTA_DATA ( rta [ RTA_IIF - 1 ] ) , sizeof ( int ) ) ;
if ( fl . iif ) {
struct net_device * dev ;
2007-09-17 22:56:21 +04:00
if ( ( dev = dev_get_by_index ( & init_net , fl . iif ) ) = = NULL ) {
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
return - ENODEV ;
}
if ( ! dev - > dn_ptr ) {
dev_put ( dev ) ;
kfree_skb ( skb ) ;
return - ENODEV ;
}
skb - > protocol = __constant_htons ( ETH_P_DNA_RT ) ;
skb - > dev = dev ;
cb - > src = fl . fld_src ;
cb - > dst = fl . fld_dst ;
local_bh_disable ( ) ;
err = dn_route_input ( skb ) ;
local_bh_enable ( ) ;
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 ) ) ;
fl . oif = oif ;
err = dn_route_output_key ( ( struct dst_entry * * ) & rt , & fl , 0 ) ;
}
if ( skb - > dev )
dev_put ( skb - > dev ) ;
skb - > dev = NULL ;
if ( err )
goto out_free ;
skb - > dst = & rt - > u . dst ;
if ( rtm - > rtm_flags & RTM_F_NOTIFY )
rt - > rt_flags | = RTCF_NOTIFY ;
2005-06-19 09:54:12 +04:00
err = dn_rt_fill_info ( skb , NETLINK_CB ( in_skb ) . pid , nlh - > nlmsg_seq , RTM_NEWROUTE , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( err = = 0 )
goto out_free ;
if ( err < 0 ) {
err = - EMSGSIZE ;
goto out_free ;
}
2007-11-20 09:26:51 +03:00
return rtnl_unicast ( skb , & init_net , NETLINK_CB ( in_skb ) . pid ) ;
2005-04-17 02:20:36 +04:00
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 )
{
2007-11-30 16:21:31 +03:00
struct net * net = skb - > sk - > sk_net ;
2005-04-17 02:20:36 +04:00
struct dn_route * rt ;
int h , s_h ;
int idx , s_idx ;
2007-11-30 16:21:31 +03:00
if ( net ! = & init_net )
return 0 ;
2005-04-17 02:20:36 +04:00
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_rt_hash_mask ; h + + ) {
if ( h < s_h )
continue ;
if ( h > s_h )
s_idx = 0 ;
rcu_read_lock_bh ( ) ;
for ( rt = rcu_dereference ( dn_rt_hash_table [ h ] . chain ) , idx = 0 ;
rt ;
2007-02-10 03:25:52 +03:00
rt = rcu_dereference ( rt - > u . dst . dn_next ) , idx + + ) {
2005-04-17 02:20:36 +04:00
if ( idx < s_idx )
continue ;
skb - > dst = dst_clone ( & rt - > u . dst ) ;
if ( dn_rt_fill_info ( skb , NETLINK_CB ( cb - > skb ) . pid ,
2007-02-09 17:24:40 +03:00
cb - > nlh - > nlmsg_seq , RTM_NEWROUTE ,
2005-06-19 09:54:12 +04:00
1 , NLM_F_MULTI ) < = 0 ) {
2005-04-17 02:20:36 +04:00
dst_release ( xchg ( & skb - > dst , NULL ) ) ;
rcu_read_unlock_bh ( ) ;
goto done ;
}
dst_release ( xchg ( & skb - > dst , NULL ) ) ;
}
rcu_read_unlock_bh ( ) ;
}
done :
cb - > args [ 0 ] = h ;
cb - > args [ 1 ] = idx ;
return skb - > len ;
}
# ifdef CONFIG_PROC_FS
struct dn_rt_cache_iter_state {
int bucket ;
} ;
static struct dn_route * dn_rt_cache_get_first ( struct seq_file * seq )
{
struct dn_route * rt = NULL ;
struct dn_rt_cache_iter_state * s = seq - > private ;
for ( s - > bucket = dn_rt_hash_mask ; s - > bucket > = 0 ; - - s - > bucket ) {
rcu_read_lock_bh ( ) ;
rt = dn_rt_hash_table [ s - > bucket ] . chain ;
if ( rt )
break ;
rcu_read_unlock_bh ( ) ;
}
2008-01-11 09:35:21 +03:00
return rcu_dereference ( rt ) ;
2005-04-17 02:20:36 +04:00
}
static struct dn_route * dn_rt_cache_get_next ( struct seq_file * seq , struct dn_route * rt )
{
2008-01-11 09:35:21 +03:00
struct dn_rt_cache_iter_state * s = seq - > private ;
2005-04-17 02:20:36 +04:00
2007-02-10 03:25:52 +03:00
rt = rt - > u . dst . dn_next ;
2005-04-17 02:20:36 +04:00
while ( ! rt ) {
rcu_read_unlock_bh ( ) ;
if ( - - s - > bucket < 0 )
break ;
rcu_read_lock_bh ( ) ;
rt = dn_rt_hash_table [ s - > bucket ] . chain ;
}
2008-01-11 09:35:21 +03:00
return rcu_dereference ( rt ) ;
2005-04-17 02:20:36 +04:00
}
static void * dn_rt_cache_seq_start ( struct seq_file * seq , loff_t * pos )
{
struct dn_route * rt = dn_rt_cache_get_first ( seq ) ;
if ( rt ) {
while ( * pos & & ( rt = dn_rt_cache_get_next ( seq , rt ) ) )
- - * pos ;
}
return * pos ? NULL : rt ;
}
static void * dn_rt_cache_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
struct dn_route * rt = dn_rt_cache_get_next ( seq , v ) ;
+ + * pos ;
return rt ;
}
static void dn_rt_cache_seq_stop ( struct seq_file * seq , void * v )
{
if ( v )
rcu_read_unlock_bh ( ) ;
}
static int dn_rt_cache_seq_show ( struct seq_file * seq , void * v )
{
struct dn_route * rt = v ;
char buf1 [ DN_ASCBUF_LEN ] , buf2 [ DN_ASCBUF_LEN ] ;
seq_printf ( seq , " %-8s %-7s %-7s %04d %04d %04d \n " ,
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 . __refcnt ) ,
rt - > u . dst . __use ,
( int ) dst_metric ( & rt - > u . dst , RTAX_RTT ) ) ;
return 0 ;
2007-02-09 17:24:40 +03:00
}
2005-04-17 02:20:36 +04:00
2007-07-11 10:07:31 +04:00
static const struct seq_operations dn_rt_cache_seq_ops = {
2005-04-17 02:20:36 +04:00
. start = dn_rt_cache_seq_start ,
. next = dn_rt_cache_seq_next ,
. stop = dn_rt_cache_seq_stop ,
. show = dn_rt_cache_seq_show ,
} ;
static int dn_rt_cache_seq_open ( struct inode * inode , struct file * file )
{
2007-10-10 13:30:23 +04:00
return seq_open_private ( file , & dn_rt_cache_seq_ops ,
sizeof ( struct dn_rt_cache_iter_state ) ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-12 11:55:35 +03:00
static const struct file_operations dn_rt_cache_seq_fops = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. open = dn_rt_cache_seq_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release_private ,
} ;
# endif /* CONFIG_PROC_FS */
void __init dn_route_init ( void )
{
int i , goal , order ;
2006-08-27 06:25:52 +04:00
dn_dst_ops . kmem_cachep =
kmem_cache_create ( " dn_dst_cache " , sizeof ( struct dn_route ) , 0 ,
2007-07-20 05:11:58 +04:00
SLAB_HWCACHE_ALIGN | SLAB_PANIC , NULL ) ;
2008-01-24 08:20:07 +03:00
setup_timer ( & dn_route_timer , dn_dst_check_expire , 0 ) ;
2005-04-17 02:20:36 +04:00
dn_route_timer . expires = jiffies + decnet_dst_gc_interval * HZ ;
add_timer ( & dn_route_timer ) ;
goal = num_physpages > > ( 26 - PAGE_SHIFT ) ;
for ( order = 0 ; ( 1UL < < order ) < goal ; order + + )
/* NOTHING */ ;
2007-02-09 17:24:40 +03:00
/*
* Only want 1024 entries max , since the table is very , very unlikely
* to be larger than that .
*/
while ( order & & ( ( ( ( 1UL < < order ) * PAGE_SIZE ) /
sizeof ( struct dn_rt_hash_bucket ) ) > = 2048 ) )
order - - ;
do {
dn_rt_hash_mask = ( 1UL < < order ) * PAGE_SIZE /
sizeof ( struct dn_rt_hash_bucket ) ;
while ( dn_rt_hash_mask & ( dn_rt_hash_mask - 1 ) )
dn_rt_hash_mask - - ;
dn_rt_hash_table = ( struct dn_rt_hash_bucket * )
__get_free_pages ( GFP_ATOMIC , order ) ;
} while ( dn_rt_hash_table = = NULL & & - - order > 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! dn_rt_hash_table )
2007-02-09 17:24:40 +03:00
panic ( " Failed to allocate DECnet route cache hash table \n " ) ;
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:40 +03:00
printk ( KERN_INFO
" DECnet: Routing cache hash table of %u buckets, %ldKbytes \n " ,
dn_rt_hash_mask ,
2005-04-17 02:20:36 +04:00
( long ) ( dn_rt_hash_mask * sizeof ( struct dn_rt_hash_bucket ) ) / 1024 ) ;
dn_rt_hash_mask - - ;
2007-02-09 17:24:40 +03:00
for ( i = 0 ; i < = dn_rt_hash_mask ; i + + ) {
spin_lock_init ( & dn_rt_hash_table [ i ] . lock ) ;
dn_rt_hash_table [ i ] . chain = NULL ;
}
2005-04-17 02:20:36 +04:00
2007-02-09 17:24:40 +03:00
dn_dst_ops . gc_thresh = ( dn_rt_hash_mask + 1 ) ;
2005-04-17 02:20:36 +04:00
2007-09-12 14:01:34 +04:00
proc_net_fops_create ( & init_net , " decnet_cache " , S_IRUGO , & dn_rt_cache_seq_fops ) ;
2007-03-22 21:57:46 +03:00
# ifdef CONFIG_DECNET_ROUTER
rtnl_register ( PF_DECnet , RTM_GETROUTE , dn_cache_getroute , dn_fib_dump ) ;
# else
rtnl_register ( PF_DECnet , RTM_GETROUTE , dn_cache_getroute ,
dn_cache_dump ) ;
# endif
2005-04-17 02:20:36 +04:00
}
void __exit dn_route_cleanup ( void )
{
del_timer ( & dn_route_timer ) ;
dn_run_flush ( 0 ) ;
2007-09-12 14:01:34 +04:00
proc_net_remove ( & init_net , " decnet_cache " ) ;
2005-04-17 02:20:36 +04:00
}