📄 loopback.c
字号:
/* loopback.c * linqianghe@163.com * 2006-09-18 */#include "log.h"#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>#include "dev_dummy.h"#include "dummy_ipv4.h"#include "dev.h"MODULE_LICENSE("Dual BSD/GPL");static DEFINE_PER_CPU(struct net_device_stats, myloopback_stats);#define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16)#ifdef LOOPBACK_TSOstatic void myemulate_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; mynetif_rx(nskb); offset += frag_size; seq += frag_size; id++; } dev_kfree_skb(skb);}#endif /* LOOPBACK_TSO */static int myloopback_xmit(struct sk_buff *skb, struct net_device *dev){ struct net_device_stats *lb_stats; PR_DEBUG( "myloopback_xmit!\n" ); skb_orphan(skb); if( strncmp( skb->data, DUMMY_HEADER, 5 ) == 0 ){ skb->protocol = __constant_htons(ETH_P_IP); skb->dev = dev; PR_DEBUG( "this is a dummy packet!, protocol is: %x\n", skb->protocol ); }else{ skb->protocol = eth_type_trans(skb,dev); skb->dev = dev;#ifndef LOOPBACK_MUST_CHECKSUM skb->ip_summed = CHECKSUM_UNNECESSARY;#endif#ifdef LOOPBACK_TSO if (skb_shinfo(skb)->tso_size) { BUG_ON(skb->protocol != htons(ETH_P_IP)); BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); myemulate_large_send_offload(skb); return 0; }#endif } dev->last_rx = jiffies; lb_stats = &per_cpu( myloopback_stats, get_cpu()); lb_stats->rx_bytes += skb->len; lb_stats->tx_bytes = lb_stats->rx_bytes; lb_stats->rx_packets++; lb_stats->tx_packets = lb_stats->rx_packets; put_cpu(); mynetif_rx( skb ); return(0);}static struct net_device_stats *myget_stats(struct net_device *dev){ struct net_device_stats *stats = dev->priv; int i; PR_DEBUG( "myget_stats!\n" ); 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( myloopback_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;}static u32 myloopback_get_link(struct net_device *dev){ return 1;}static struct ethtool_ops myloopback_ethtool_ops = { .get_link = myloopback_get_link, .get_tso = ethtool_op_get_tso, .set_tso = ethtool_op_set_tso,};int myeth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len){ struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN); PR_DEBUG( "type: %d\n", type ); if(type!=ETH_P_802_3) eth->h_proto = htons(type); else eth->h_proto = htons(len); if(!saddr) saddr = dev->dev_addr; memcpy( eth->h_source, saddr, dev->addr_len ); if(daddr){ memcpy(eth->h_dest,daddr,dev->addr_len); return ETH_HLEN; } if( dev->flags & (IFF_LOOPBACK|IFF_NOARP) ) { memset( eth->h_dest, 0, dev->addr_len ); return ETH_HLEN; } return -ETH_HLEN;}int myeth_header_cache(struct neighbour *neigh, struct hh_cache *hh){ unsigned short type = hh->hh_type; struct ethhdr *eth; struct net_device *dev = neigh->dev; PR_DEBUG( "eth->h_proto: %d\n", type ); eth = (struct ethhdr*) (((u8*)hh->hh_data) + (HH_DATA_OFF(sizeof(*eth)))); if (type == __constant_htons(ETH_P_802_3)) return -1; eth->h_proto = type; memcpy(eth->h_source, dev->dev_addr, dev->addr_len); memcpy(eth->h_dest, neigh->ha, dev->addr_len); hh->hh_len = ETH_HLEN; return 0;}void myeth_header_cache_update(struct hh_cache *hh, struct net_device *dev, unsigned char * haddr){ PR_DEBUG( "eth_header_cache_update!\n" ); memcpy(((u8*)hh->hh_data) + HH_DATA_OFF(sizeof(struct ethhdr)), haddr, dev->addr_len);}int myeth_rebuild_header(struct sk_buff *skb){ struct ethhdr *eth = (struct ethhdr *)skb->data; struct net_device *dev = skb->dev; PR_DEBUG( "the eth->h_proto: %d\n", eth->h_proto ); switch (eth->h_proto){#ifdef CONFIG_INET case __constant_htons(ETH_P_IP): return 0;//arp_find(eth->h_dest, skb);#endif default: PR_DEBUG( "%s: unable to resolve type %X addresses.\n", dev->name, (int)eth->h_proto); memcpy(eth->h_source, dev->dev_addr, dev->addr_len); break; } return 0;}struct net_device myloopback_dev = { .name = "mylo", .mtu = (16 * 1024) + 20 + 20 + 12, .hard_start_xmit = myloopback_xmit, .hard_header = myeth_header, .hard_header_cache = myeth_header_cache, .header_cache_update = myeth_header_cache_update, .hard_header_len = ETH_HLEN, /* 14*/ .addr_len = ETH_ALEN, /* 6*/ .tx_queue_len = 0, .type = ARPHRD_LOOPBACK, /* 0x0001*/ .rebuild_header = myeth_rebuild_header, .flags = IFF_LOOPBACK, .features = NETIF_F_SG | NETIF_F_FRAGLIST#ifdef LOOPBACK_TSO | NETIF_F_TSO#endif | NETIF_F_NO_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX, .ethtool_ops = &myloopback_ethtool_ops,};int __init myloopback_init(void){ struct net_device_stats *stats; PR_DEBUG("loading the module mylo...\n"); stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL); if (stats) { memset( stats, 0, sizeof(struct net_device_stats) ); myloopback_dev.priv = stats; myloopback_dev.get_stats = &myget_stats; } PR_DEBUG("loading the module mylo finished.\n"); return register_netdev( &myloopback_dev );};void __exit myloopback_exit(void){ PR_DEBUG( "unloading the module mylo...\n " ); unregister_netdev( &myloopback_dev ); if( myloopback_dev.priv ) kfree( myloopback_dev.priv ); PR_DEBUG( "unloading the module mylo finished.\n" );}module_init( myloopback_init );module_exit( myloopback_exit );EXPORT_SYMBOL_GPL( myloopback_dev );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -