2005-04-17 02:20:36 +04:00
/*
* ipddp . c : IP to Appletalk - IP Encapsulation driver for Linux
* Appletalk - IP to IP Decapsulation driver for Linux
*
* Authors :
* - DDP - IP Encap by : Bradford W . Johnson < johns393 @ maroon . tc . umn . edu >
* - DDP - IP Decap by : Jay Schulist < jschlst @ samba . org >
*
* Derived from :
* - Almost all code already existed in net / appletalk / ddp . c I just
* moved / reorginized it into a driver file . Original IP - over - DDP code
* was done by Bradford W . Johnson < johns393 @ maroon . tc . umn . edu >
* - skeleton . c : A network driver outline for linux .
* Written 1993 - 94 by Donald Becker .
* - dummy . c : A dummy net driver . By Nick Holloway .
* - MacGate : A user space Daemon for Appletalk - IP Decap for
* Linux by Jay Schulist < jschlst @ samba . org >
*
* Copyright 1993 United States Government as represented by the
* Director , National Security Agency .
*
* This software may be used and distributed according to the terms
* of the GNU General Public License , incorporated herein by reference .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/ip.h>
# include <linux/atalk.h>
# include <linux/if_arp.h>
# include <net/route.h>
# include <asm/uaccess.h>
# include "ipddp.h" /* Our stuff */
static const char version [ ] = KERN_INFO " ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu> \n " ;
static struct ipddp_route * ipddp_route_list ;
2009-05-28 03:56:47 +04:00
static DEFINE_SPINLOCK ( ipddp_route_lock ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_IPDDP_ENCAP
static int ipddp_mode = IPDDP_ENCAP ;
# else
static int ipddp_mode = IPDDP_DECAP ;
# endif
/* Index to functions, as function prototypes. */
2009-08-31 23:50:56 +04:00
static netdev_tx_t ipddp_xmit ( struct sk_buff * skb ,
struct net_device * dev ) ;
2005-04-17 02:20:36 +04:00
static int ipddp_create ( struct ipddp_route * new_rt ) ;
static int ipddp_delete ( struct ipddp_route * rt ) ;
2009-05-28 03:56:47 +04:00
static struct ipddp_route * __ipddp_find_route ( struct ipddp_route * rt ) ;
2005-04-17 02:20:36 +04:00
static int ipddp_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd ) ;
2009-01-08 04:22:19 +03:00
static const struct net_device_ops ipddp_netdev_ops = {
. ndo_start_xmit = ipddp_xmit ,
. ndo_do_ioctl = ipddp_ioctl ,
. ndo_change_mtu = eth_change_mtu ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_validate_addr = eth_validate_addr ,
} ;
2005-04-17 02:20:36 +04:00
static struct net_device * __init ipddp_init ( void )
{
static unsigned version_printed ;
struct net_device * dev ;
int err ;
2009-01-08 04:22:19 +03:00
dev = alloc_etherdev ( 0 ) ;
2005-04-17 02:20:36 +04:00
if ( ! dev )
return ERR_PTR ( - ENOMEM ) ;
net: release dst entry in dev_hard_start_xmit()
One point of contention in high network loads is the dst_release() performed
when a transmited skb is freed. This is because NIC tx completion calls
dev_kree_skb() long after original call to dev_queue_xmit(skb).
CPU cache is cold and the atomic op in dst_release() stalls. On SMP, this is
quite visible if one CPU is 100% handling softirqs for a network device,
since dst_clone() is done by other cpus, involving cache line ping pongs.
It seems right place to release dst is in dev_hard_start_xmit(), for most
devices but ones that are virtual, and some exceptions.
David Miller suggested to define a new device flag, set in alloc_netdev_mq()
(so that most devices set it at init time), and carefuly unset in devices
which dont want a NULL skb->dst in their ndo_start_xmit().
List of devices that must clear this flag is :
- loopback device, because it calls netif_rx() and quoting Patrick :
"ip_route_input() doesn't accept loopback addresses, so loopback packets
already need to have a dst_entry attached."
- appletalk/ipddp.c : needs skb->dst in its xmit function
- And all devices that call again dev_queue_xmit() from their xmit function
(as some classifiers need skb->dst) : bonding, vlan, macvlan, eql, ifb, hdlc_fr
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2009-05-19 09:19:19 +04:00
dev - > priv_flags & = ~ IFF_XMIT_DST_RELEASE ;
2005-04-17 02:20:36 +04:00
strcpy ( dev - > name , " ipddp%d " ) ;
if ( version_printed + + = = 0 )
printk ( version ) ;
/* Initalize the device structure. */
2009-01-08 04:22:19 +03:00
dev - > netdev_ops = & ipddp_netdev_ops ;
2005-04-17 02:20:36 +04:00
dev - > type = ARPHRD_IPDDP ; /* IP over DDP tunnel */
dev - > mtu = 585 ;
dev - > flags | = IFF_NOARP ;
/*
* The worst case header we will need is currently a
* ethernet header ( 14 bytes ) and a ddp header ( sizeof ddpehdr + 1 )
* We send over SNAP so that takes another 8 bytes .
*/
dev - > hard_header_len = 14 + 8 + sizeof ( struct ddpehdr ) + 1 ;
err = register_netdev ( dev ) ;
if ( err ) {
free_netdev ( dev ) ;
return ERR_PTR ( err ) ;
}
/* Let the user now what mode we are in */
if ( ipddp_mode = = IPDDP_ENCAP )
printk ( " %s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu> \n " ,
dev - > name ) ;
if ( ipddp_mode = = IPDDP_DECAP )
printk ( " %s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org> \n " ,
dev - > name ) ;
return dev ;
}
/*
* Transmit LLAP / ELAP frame using aarp_send_ddp .
*/
2009-08-31 23:50:56 +04:00
static netdev_tx_t ipddp_xmit ( struct sk_buff * skb , struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2009-06-02 09:14:27 +04:00
__be32 paddr = skb_rtable ( skb ) - > rt_gateway ;
2005-04-17 02:20:36 +04:00
struct ddpehdr * ddp ;
struct ipddp_route * rt ;
struct atalk_addr * our_addr ;
2009-05-28 03:56:47 +04:00
spin_lock ( & ipddp_route_lock ) ;
2005-04-17 02:20:36 +04:00
/*
* Find appropriate route to use , based only on IP number .
*/
for ( rt = ipddp_route_list ; rt ! = NULL ; rt = rt - > next )
{
if ( rt - > ip = = paddr )
break ;
}
2009-05-28 03:56:47 +04:00
if ( rt = = NULL ) {
spin_unlock ( & ipddp_route_lock ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2009-05-28 03:56:47 +04:00
}
2005-04-17 02:20:36 +04:00
our_addr = atalk_find_dev_addr ( rt - > dev ) ;
if ( ipddp_mode = = IPDDP_DECAP )
/*
* Pull off the excess room that should not be there .
* This is due to a hard - header problem . This is the
* quick fix for now though , till it breaks .
*/
skb_pull ( skb , 35 - ( sizeof ( struct ddpehdr ) + 1 ) ) ;
/* Create the Extended DDP header */
ddp = ( struct ddpehdr * ) skb - > data ;
2006-09-27 08:22:08 +04:00
ddp - > deh_len_hops = htons ( skb - > len + ( 1 < < 10 ) ) ;
2005-04-17 02:20:36 +04:00
ddp - > deh_sum = 0 ;
/*
* For Localtalk we need aarp_send_ddp to strip the
* long DDP header and place a shot DDP header on it .
*/
if ( rt - > dev - > type = = ARPHRD_LOCALTLK )
{
ddp - > deh_dnet = 0 ; /* FIXME more hops?? */
ddp - > deh_snet = 0 ;
}
else
{
ddp - > deh_dnet = rt - > at . s_net ; /* FIXME more hops?? */
ddp - > deh_snet = our_addr - > s_net ;
}
ddp - > deh_dnode = rt - > at . s_node ;
ddp - > deh_snode = our_addr - > s_node ;
ddp - > deh_dport = 72 ;
ddp - > deh_sport = 72 ;
* ( ( __u8 * ) ( ddp + 1 ) ) = 22 ; /* ddp type = IP */
skb - > protocol = htons ( ETH_P_ATALK ) ; /* Protocol has changed */
2009-01-08 04:22:19 +03:00
dev - > stats . tx_packets + + ;
dev - > stats . tx_bytes + = skb - > len ;
2005-04-17 02:20:36 +04:00
if ( aarp_send_ddp ( rt - > dev , skb , & rt - > at , NULL ) < 0 )
dev_kfree_skb ( skb ) ;
2009-05-28 03:56:47 +04:00
spin_unlock ( & ipddp_route_lock ) ;
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
/*
* Create a routing entry . We first verify that the
* record does not already exist . If it does we return - EEXIST
*/
static int ipddp_create ( struct ipddp_route * new_rt )
{
2006-12-13 11:35:56 +03:00
struct ipddp_route * rt = kmalloc ( sizeof ( * rt ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( rt = = NULL )
return - ENOMEM ;
rt - > ip = new_rt - > ip ;
rt - > at = new_rt - > at ;
rt - > next = NULL ;
if ( ( rt - > dev = atrtr_get_dev ( & rt - > at ) ) = = NULL ) {
kfree ( rt ) ;
return - ENETUNREACH ;
}
2009-05-28 03:56:47 +04:00
spin_lock_bh ( & ipddp_route_lock ) ;
if ( __ipddp_find_route ( rt ) ) {
spin_unlock_bh ( & ipddp_route_lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( rt ) ;
return - EEXIST ;
}
rt - > next = ipddp_route_list ;
ipddp_route_list = rt ;
2009-05-28 03:56:47 +04:00
spin_unlock_bh ( & ipddp_route_lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/*
* Delete a route , we only delete a FULL match .
* If route does not exist we return - ENOENT .
*/
static int ipddp_delete ( struct ipddp_route * rt )
{
struct ipddp_route * * r = & ipddp_route_list ;
struct ipddp_route * tmp ;
2009-05-28 03:56:47 +04:00
spin_lock_bh ( & ipddp_route_lock ) ;
2005-04-17 02:20:36 +04:00
while ( ( tmp = * r ) ! = NULL )
{
if ( tmp - > ip = = rt - > ip
& & tmp - > at . s_net = = rt - > at . s_net
& & tmp - > at . s_node = = rt - > at . s_node )
{
* r = tmp - > next ;
2009-05-28 03:56:47 +04:00
spin_unlock_bh ( & ipddp_route_lock ) ;
2005-04-17 02:20:36 +04:00
kfree ( tmp ) ;
return 0 ;
}
r = & tmp - > next ;
}
2009-05-28 03:56:47 +04:00
spin_unlock_bh ( & ipddp_route_lock ) ;
2005-04-17 02:20:36 +04:00
return ( - ENOENT ) ;
}
/*
* Find a routing entry , we only return a FULL match
*/
2009-05-28 03:56:47 +04:00
static struct ipddp_route * __ipddp_find_route ( struct ipddp_route * rt )
2005-04-17 02:20:36 +04:00
{
struct ipddp_route * f ;
for ( f = ipddp_route_list ; f ! = NULL ; f = f - > next )
{
if ( f - > ip = = rt - > ip
& & f - > at . s_net = = rt - > at . s_net
& & f - > at . s_node = = rt - > at . s_node )
return ( f ) ;
}
return ( NULL ) ;
}
static int ipddp_ioctl ( struct net_device * dev , struct ifreq * ifr , int cmd )
{
struct ipddp_route __user * rt = ifr - > ifr_data ;
2009-05-28 03:56:47 +04:00
struct ipddp_route rcp , rcp2 , * rp ;
2005-04-17 02:20:36 +04:00
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
if ( copy_from_user ( & rcp , rt , sizeof ( rcp ) ) )
return - EFAULT ;
switch ( cmd )
{
case SIOCADDIPDDPRT :
return ( ipddp_create ( & rcp ) ) ;
case SIOCFINDIPDDPRT :
2009-05-28 03:56:47 +04:00
spin_lock_bh ( & ipddp_route_lock ) ;
rp = __ipddp_find_route ( & rcp ) ;
if ( rp )
memcpy ( & rcp2 , rp , sizeof ( rcp2 ) ) ;
spin_unlock_bh ( & ipddp_route_lock ) ;
if ( rp ) {
if ( copy_to_user ( rt , & rcp2 ,
sizeof ( struct ipddp_route ) ) )
return - EFAULT ;
return 0 ;
} else
return - ENOENT ;
2005-04-17 02:20:36 +04:00
case SIOCDELIPDDPRT :
return ( ipddp_delete ( & rcp ) ) ;
default :
return - EINVAL ;
}
}
static struct net_device * dev_ipddp ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( ipddp_mode , int , 0 ) ;
static int __init ipddp_init_module ( void )
{
dev_ipddp = ipddp_init ( ) ;
if ( IS_ERR ( dev_ipddp ) )
return PTR_ERR ( dev_ipddp ) ;
return 0 ;
}
static void __exit ipddp_cleanup_module ( void )
{
struct ipddp_route * p ;
unregister_netdev ( dev_ipddp ) ;
free_netdev ( dev_ipddp ) ;
while ( ipddp_route_list ) {
p = ipddp_route_list - > next ;
kfree ( ipddp_route_list ) ;
ipddp_route_list = p ;
}
}
module_init ( ipddp_init_module ) ;
module_exit ( ipddp_cleanup_module ) ;