2005-04-17 02:20:36 +04:00
/*
* INET An implementation of the TCP / IP protocol suite for the LINUX
* operating system . INET is implemented using the BSD Socket
* interface as the means of communication with the user level .
*
* Pseudo - driver for the loopback interface .
*
* Version : @ ( # ) loopback . c 1.0 .4 b 08 / 16 / 93
*
2005-05-06 03:16:16 +04:00
* Authors : Ross Biro
2005-04-17 02:20:36 +04:00
* Fred N . van Kempen , < waltje @ uWalt . NL . Mugnet . ORG >
* Donald Becker , < becker @ scyld . com >
*
* Alan Cox : Fixed oddments for NET3 .014
* Alan Cox : Rejig for NET3 .029 snap # 3
* Alan Cox : Fixed NET3 .029 bugs and sped up
* Larry McVoy : Tiny tweak to double performance
* Alan Cox : Backed out LMV ' s tweak - the linux mm
* can ' t take it . . .
* Michael Griffith : Don ' t bother computing the checksums
* on packets received on the loopback
* interface .
* Alexey Kuznetsov : Potential hang under some extreme
* cases removed .
*
* 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 ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/jiffies.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/fs.h>
# include <linux/types.h>
# include <linux/string.h>
# include <linux/socket.h>
# include <linux/errno.h>
# include <linux/fcntl.h>
# include <linux/in.h>
# include <linux/init.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/io.h>
# include <linux/inet.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/ethtool.h>
# include <net/sock.h>
# include <net/checksum.h>
# include <linux/if_ether.h> /* For the statistics structure. */
# include <linux/if_arp.h> /* For ARPHRD_ETHER */
# include <linux/ip.h>
# include <linux/tcp.h>
# include <linux/percpu.h>
2006-10-19 07:51:57 +04:00
struct pcpu_lstats {
unsigned long packets ;
unsigned long bytes ;
} ;
static DEFINE_PER_CPU ( struct pcpu_lstats , pcpu_lstats ) ;
2005-04-17 02:20:36 +04:00
# define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16)
/* KISS: just allocate small chunks and copy bits.
*
* So , in fact , this is documentation , explaining what we expect
* of largesending device modulo TCP checksum , which is ignored for loopback .
*/
2005-07-03 05:28:21 +04:00
# ifdef LOOPBACK_TSO
2005-04-17 02:20:36 +04:00
static void emulate_large_send_offload ( struct sk_buff * skb )
{
struct iphdr * iph = skb - > nh . iph ;
struct tcphdr * th = ( struct tcphdr * ) ( skb - > nh . raw + ( iph - > ihl * 4 ) ) ;
unsigned int doffset = ( iph - > ihl + th - > doff ) * 4 ;
2006-06-22 13:40:14 +04:00
unsigned int mtu = skb_shinfo ( skb ) - > gso_size + doffset ;
2005-04-17 02:20:36 +04:00
unsigned int offset = 0 ;
u32 seq = ntohl ( th - > seq ) ;
u16 id = ntohs ( iph - > id ) ;
while ( offset + doffset < skb - > len ) {
unsigned int frag_size = min ( mtu , skb - > len - offset ) - doffset ;
struct sk_buff * nskb = alloc_skb ( mtu + 32 , GFP_ATOMIC ) ;
if ( ! nskb )
break ;
skb_reserve ( nskb , 32 ) ;
2007-03-10 18:30:58 +03:00
skb_set_mac_header ( nskb , - ETH_HLEN ) ;
2005-04-17 02:20:36 +04:00
nskb - > nh . raw = nskb - > data ;
iph = nskb - > nh . iph ;
memcpy ( nskb - > data , skb - > nh . raw , doffset ) ;
if ( skb_copy_bits ( skb ,
doffset + offset ,
nskb - > data + doffset ,
frag_size ) )
BUG ( ) ;
skb_put ( nskb , doffset + frag_size ) ;
nskb - > ip_summed = CHECKSUM_UNNECESSARY ;
nskb - > dev = skb - > dev ;
nskb - > priority = skb - > priority ;
nskb - > protocol = skb - > protocol ;
nskb - > dst = dst_clone ( skb - > dst ) ;
memcpy ( nskb - > cb , skb - > cb , sizeof ( skb - > cb ) ) ;
nskb - > pkt_type = skb - > pkt_type ;
th = ( struct tcphdr * ) ( nskb - > nh . raw + iph - > ihl * 4 ) ;
iph - > tot_len = htons ( frag_size + doffset ) ;
iph - > id = htons ( id ) ;
iph - > check = 0 ;
iph - > check = ip_fast_csum ( ( unsigned char * ) iph , iph - > ihl ) ;
th - > seq = htonl ( seq ) ;
if ( offset + doffset + frag_size < skb - > len )
th - > fin = th - > psh = 0 ;
netif_rx ( nskb ) ;
offset + = frag_size ;
seq + = frag_size ;
id + + ;
}
dev_kfree_skb ( skb ) ;
}
2005-07-03 05:28:21 +04:00
# endif /* LOOPBACK_TSO */
2005-04-17 02:20:36 +04:00
/*
* The higher levels take care of making this non - reentrant ( it ' s
* called with bh ' s disabled ) .
*/
static int loopback_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2006-10-19 07:51:57 +04:00
struct pcpu_lstats * lb_stats ;
2005-04-17 02:20:36 +04:00
skb_orphan ( skb ) ;
2005-07-03 05:28:23 +04:00
skb - > protocol = eth_type_trans ( skb , dev ) ;
2005-04-17 02:20:36 +04:00
# ifndef LOOPBACK_MUST_CHECKSUM
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
# endif
2005-07-03 05:28:21 +04:00
# ifdef LOOPBACK_TSO
2006-07-09 00:34:32 +04:00
if ( skb_is_gso ( skb ) ) {
2005-04-17 02:20:36 +04:00
BUG_ON ( skb - > protocol ! = htons ( ETH_P_IP ) ) ;
BUG_ON ( skb - > nh . iph - > protocol ! = IPPROTO_TCP ) ;
emulate_large_send_offload ( skb ) ;
return 0 ;
}
2005-07-03 05:28:21 +04:00
# endif
2005-04-17 02:20:36 +04:00
dev - > last_rx = jiffies ;
2006-10-20 11:32:41 +04:00
/* it's OK to use __get_cpu_var() because BHs are off */
lb_stats = & __get_cpu_var ( pcpu_lstats ) ;
2006-10-19 07:51:57 +04:00
lb_stats - > bytes + = skb - > len ;
lb_stats - > packets + + ;
2005-04-17 02:20:36 +04:00
netif_rx ( skb ) ;
2006-10-20 11:32:41 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-09-28 07:33:34 +04:00
static struct net_device_stats loopback_stats ;
2005-04-17 02:20:36 +04:00
static struct net_device_stats * get_stats ( struct net_device * dev )
{
2006-09-28 07:33:34 +04:00
struct net_device_stats * stats = & loopback_stats ;
2006-10-19 07:51:57 +04:00
unsigned long bytes = 0 ;
unsigned long packets = 0 ;
2005-04-17 02:20:36 +04:00
int i ;
2006-03-28 13:56:37 +04:00
for_each_possible_cpu ( i ) {
2006-10-19 07:51:57 +04:00
const struct pcpu_lstats * lb_stats ;
2005-04-17 02:20:36 +04:00
2006-10-19 07:51:57 +04:00
lb_stats = & per_cpu ( pcpu_lstats , i ) ;
bytes + = lb_stats - > bytes ;
packets + = lb_stats - > packets ;
2005-04-17 02:20:36 +04:00
}
2006-10-19 07:51:57 +04:00
stats - > rx_packets = packets ;
stats - > tx_packets = packets ;
stats - > rx_bytes = bytes ;
stats - > tx_bytes = bytes ;
2005-04-17 02:20:36 +04:00
return stats ;
}
2006-09-28 07:33:34 +04:00
static u32 always_on ( struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
return 1 ;
}
2006-09-13 22:30:00 +04:00
static const struct ethtool_ops loopback_ethtool_ops = {
2006-09-28 07:33:34 +04:00
. get_link = always_on ,
2005-04-17 02:20:36 +04:00
. get_tso = ethtool_op_get_tso ,
. set_tso = ethtool_op_set_tso ,
2006-09-28 07:33:34 +04:00
. get_tx_csum = always_on ,
. get_sg = always_on ,
. get_rx_csum = always_on ,
2005-04-17 02:20:36 +04:00
} ;
2006-09-28 07:33:34 +04:00
/*
* The loopback device is special . There is only one instance and
* it is statically allocated . Don ' t do this for other devices .
*/
2005-04-17 02:20:36 +04:00
struct net_device loopback_dev = {
. name = " lo " ,
2006-09-28 07:33:34 +04:00
. get_stats = & get_stats ,
. priv = & loopback_stats ,
2005-04-17 02:20:36 +04:00
. mtu = ( 16 * 1024 ) + 20 + 20 + 12 ,
. hard_start_xmit = loopback_xmit ,
. hard_header = eth_header ,
. hard_header_cache = eth_header_cache ,
. header_cache_update = eth_header_cache_update ,
. hard_header_len = ETH_HLEN , /* 14 */
. addr_len = ETH_ALEN , /* 6 */
. tx_queue_len = 0 ,
. type = ARPHRD_LOOPBACK , /* 0x0001*/
. rebuild_header = eth_rebuild_header ,
. flags = IFF_LOOPBACK ,
2005-07-03 05:28:23 +04:00
. features = NETIF_F_SG | NETIF_F_FRAGLIST
2005-07-03 05:28:21 +04:00
# ifdef LOOPBACK_TSO
2005-07-03 05:28:23 +04:00
| NETIF_F_TSO
2005-07-03 05:28:21 +04:00
# endif
2005-07-03 05:28:23 +04:00
| NETIF_F_NO_CSUM | NETIF_F_HIGHDMA
| NETIF_F_LLTX ,
2005-04-17 02:20:36 +04:00
. ethtool_ops = & loopback_ethtool_ops ,
} ;
2005-08-19 01:05:18 +04:00
/* Setup and register the loopback device. */
2007-01-02 11:35:48 +03:00
static int __init loopback_init ( void )
2005-04-17 02:20:36 +04:00
{
return register_netdev ( & loopback_dev ) ;
} ;
2007-01-02 11:35:48 +03:00
module_init ( loopback_init ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( loopback_dev ) ;