I am using 2.6.10 kernel version. I am learning network driver programming now. I want to create another loopback nework device. I have taken the exhisting loopback driver and modified the interface name (and some minor modifications). It created the following interface. I have assigned the same IP as that of "lo" interface.
loop Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
Now I make the "lo" interface down and try to ping localhost, but it is not working like "lo" interface. Packets are not even going to hard_start_xmit function of "loop" device.
I have also tried adding a route entry.
route add -net 127.0.0.0 netmask 255.0.0.0 dev loop
But still it not working.
Is it possible to create another loopback device?
I am also not clear about how loopback interface is working.
Is TCP/IP stack checking the flag IFF_LOOPBACK or not?
If you have some information/link about loopback device please send me.
With the following code I am trying to create another loopback device. If you can figure out where I am wrong I would be greatful.
/*
* 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.4b 08/16/93
*
* Authors: Ross Biro,
* Fred N. van Kempen,
* Donald Becker,
*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* For the statistics structure. */
#include /* For ARPHRD_ETHER */
#include
#include
#include
static DEFINE_PER_CPU(struct net_device_stats, loopback_stats);
#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.
*/
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;
unsigned int mtu = skb_shinfo(skb)->tso_size + doffset;
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);
nskb->mac.raw = nskb->data - 14;
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);
}
/*
* 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)
{
struct net_device_stats *lb_stats;
skb_orphan(skb);
skb->protocol=eth_type_trans(skb,dev);
skb->dev=dev;
#ifndef LOOPBACK_MUST_CHECKSUM
skb->ip_summed = CHECKSUM_UNNECESSARY;
#endif
if (skb_shinfo(skb)->tso_size) {
BUG_ON(skb->protocol != htons(ETH_P_IP));
BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP);
emulate_large_send_offload(skb);
return 0;
}
dev->last_rx = jiffies;
lb_stats = &per_cpu(loopback_stats, get_cpu());
lb_stats->rx_bytes += skb->len;
lb_stats->tx_bytes += skb->len;
lb_stats->rx_packets++;
lb_stats->tx_packets++;
put_cpu();
netif_rx(skb);
return(0);
}
static struct net_device_stats *get_stats(struct net_device *dev)
{
struct net_device_stats *stats = dev->priv;
int i;
if (!stats) {
return NULL;
}
memset(stats, 0, sizeof(struct net_device_stats));
for (i=0; i < NR_CPUS; i++) {
struct net_device_stats *lb_stats;
if (!cpu_possible(i))
continue;
lb_stats = &per_cpu(loopback_stats, i);
stats->rx_bytes += lb_stats->rx_bytes;
stats->tx_bytes += lb_stats->tx_bytes;
stats->rx_packets += lb_stats->rx_packets;
stats->tx_packets += lb_stats->tx_packets;
}
return stats;
}
u32 loopback_get_link(struct net_device *dev)
{
return 1;
}
static struct ethtool_ops loopback_ethtool_ops = {
.get_link = loopback_get_link,
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
};
struct net_device loop_dev = {
.name = "loop",
.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,
.features = NETIF_F_SG|NETIF_F_FRAGLIST
|NETIF_F_NO_CSUM|NETIF_F_HIGHDMA
|NETIF_F_LLTX,
.ethtool_ops = &loopback_ethtool_ops,
};
/* Setup and register the of the LOOPBACK device. */
int __init loopback_init(void)
{
struct net_device_stats *stats;
/* Can survive without statistics */
stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
if (stats) {
memset(stats, 0, sizeof(struct net_device_stats));
loop_dev.priv = stats;
loop_dev.get_stats = &get_stats;
}
return register_netdev(&loop_dev);
};
void loopback_exit(void)
{
unregister_netdev(&loop_dev);
return;
}
module_init(loopback_init);
module_exit(loopback_exit);