在内核中构造一个数据包,让指定的网卡来接收。
网上在内核中构造一个数据包然后通过一个网络接口发送出去的例子有很多,但是在内核中构造一个数据包,让网络接口来接收的方法的文章不多。
文章参考了:http://bbs.chinaunix.net/thread-1931661-1-1.html,谢谢该文的作者以及回帖者。
本人经历的项目中有个很奇怪的需求。主机有2个网络接口,要求从一个网络接口接收数据,内核对收到的数据进行修改等操作,然后由另外一个网络接口来接收这些修改后的数据。最基本的想法就是调用netif_rx函数来实现,但是本人以前一直没有成功的调用netif_rx函数,调用时出现死机,直到遇到上面那篇文章。
参考代码如下:
// eth1是客户端的网关,eth0是进行模拟接收的适配器。#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/netfilter.h>#include <linux/skbuff.h>#include <linux/ip.h>#include <linux/inet.h>#include <linux/netdevice.h> #include <linux/inetdevice.h> #include <linux/if_ether.h> #include <linux/if_packet.h>#include <linux/netfilter_ipv4.h>#include <linux/param.h>#include <linux/completion.h> // for DECLARE_COMPLETION()#include <linux/sched.h> // for daemonize() and set_current_state()#include <linux/delay.h> #include <linux/in.h>#include <linux/udp.h>#include <linux/tcp.h>#include <linux/spinlock.h>#include <linux/timer.h>#include <linux/etherdevice.h>#include <net/ip.h>#include <net/tcp.h>#include <net/udp.h>#include <net/dst.h>//MODULE_DESCRIPTION("My kernel module");MODULE_AUTHOR("root (root@localhost.localdomain)");MODULE_LICENSE("Dual BSD/GPL");//// 配置参数unsigned int ETH0= 0;unsigned int MASK0= 0;unsigned int ETH1= 0; unsigned int MASK1= 0; // 设备struct net_device *dev0 =0;struct net_device *dev1 =0;struct in_device *indev0=0;struct in_device *indev1=0;struct in_ifaddr *ifr0 =0;struct in_ifaddr *ifr1 =0;// 钩子处理函数unsigned int sendip(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)){// DEBUGprintk(KERN_EMERG "send_ip \n");return NF_ACCEPT;}// 钩子处理函数unsigned int getip(unsigned int hook, struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)){int ret;struct ethhdr *peth=NULL; struct iphdr *pip=NULL;struct iphdr *p=NULL;struct sk_buff *skb;if(pskb==NULL)return NF_DROP; if((*pskb)==NULL)return NF_DROP; // (*pskb)->len是IP包的长度(包括IP头)if(memcmp(in->name,"eth0",4)==0){}else{if(pip->daddr == ETH0){// 让eth0来接收skb = alloc_skb((*pskb)->len + 2 + ETH_HLEN,GFP_ATOMIC); // 新的IP包 skb_reserve(skb,2);skb_put(skb,(*pskb)->len + ETH_HLEN);memcpy(skb->data,(*pskb)->data - ETH_HLEN,(*pskb)->len + ETH_HLEN);skb->mac.raw = skb->data;skb->mac_len = ETH_HLEN;skb->len = (*pskb)->len + ETH_HLEN;skb->ip_summed = CHECKSUM_NONE;skb->pkt_type = PACKET_HOST;skb->protocol = __constant_htons(ETH_P_IP);skb->dev = dev0;// 关键,以前没有成功就是下面的参数没有设置。skb_shinfo(skb)->nr_frags=0;skb_shinfo(skb)->frag_list=NULL;skb_shinfo(skb)->frags[0].page=NULL;skb_pull(skb,ETH_HLEN);skb_reset_network_header(skb);ret=netif_rx(skb);dev0->last_rx = jiffies;//printk(KERN_EMERG "ret=%d , %d \n",ret,gi++);return NF_STOLEN;}}return NF_ACCEPT;}struct nf_hook_ops pre_ops ={.hook = getip,.pf = PF_INET,.hooknum = NF_IP_PRE_ROUTING,.priority = NF_IP_PRI_FIRST,};struct nf_hook_ops post_ops ={.hook = sendip,.pf = PF_INET,.hooknum = NF_IP_LOCAL_OUT,.priority = NF_IP_PRI_FIRST,};static int gatekernel_init_module(void){printk(KERN_EMERG "Module Init.\n");// 初始化设备对象dev0 = dev_get_by_name("eth0");if(!dev0){printk( KERN_EMERG "Counld not get dev0.\n");return -1;}ETH0 = inet_select_addr(dev0,0,RT_SCOPE_UNIVERSE);indev0 = (struct in_device*)dev0->ip_ptr;ifr0= indev0->ifa_list;MASK0 = ifr0->ifa_mask;dev1 = dev_get_by_name("eth1");if(!dev1){printk( KERN_EMERG "Counld not get dev1.\n");return -1;}ETH1 = inet_select_addr(dev1,0,RT_SCOPE_UNIVERSE);indev1 = (struct in_device*)dev1->ip_ptr;ifr1= indev1->ifa_list;MASK1 = ifr1->ifa_mask;nf_register_hook(&pre_ops);nf_register_hook(&post_ops);return 0;}// 反初始化static void gatekernel_exit_module(void){// 取消钩子函数nf_unregister_hook(&pre_ops);nf_unregister_hook(&post_ops);printk(KERN_EMERG "bye.\n");}module_init(gatekernel_init_module);module_exit(gatekernel_exit_module);
static unsigned int pre_routing_hook(unsigned int hooknum, struct sk_buff **pskb, const struct net_device *in, \ const struct net_device *out, int (*okfn)(struct sk_buff *)){ struct sk_buff *skb; struct sk_buff *new_skb; //unsigned char src_ip[4]; //unsigned char dst_ip[4]; // struct icmphdr *icmp_hdr; int icmp_hdr_off; // struct iphdr *ip_hdr; int ip_hdr_off; struct ethhdr *eth_hdr; struct net_device *NIC = NULL; const char nic[5] = "eth0"; // printk(KERN_INFO "inside the filter\n"); int index; int rx_ret; skb = skb_copy(*pskb, GFP_ATOMIC); NIC = dev_get_by_name(nic); if (!NIC) { return NF_ACCEPT; } if (skb->nh.iph->protocol == IPPROTO_ICMP) { // get ip header ip_hdr = (struct iphdr *)skb->nh.iph; // calcutate ip header length ip_hdr_off = ip_hdr->ihl << 2; // get icmp header icmp_hdr = (struct icmphdr *)(skb->data + ip_hdr_off); // only modify icmp request // #define ICMP_ECHO 8 /* Echo Request */ // in <linux/icmp/h> if (icmp_hdr->type == ICMP_ECHO) { new_skb = alloc_skb(skb->len + 5 + ETH_HLEN, GFP_ATOMIC); skb_reserve(new_skb, 2); skb_put(new_skb, skb->len + ETH_HLEN); memcpy(new_skb->data, skb->data - ETH_HLEN, skb->len + ETH_HLEN); new_skb->mac.raw = new_skb->data; // commented //skb_pull(new_skb, ETH_HLEN); new_skb->mac_len = ETH_HLEN; new_skb->len = skb->len + ETH_HLEN; new_skb->ip_summed = CHECKSUM_UNNECESSARY; new_skb->pkt_type = PACKET_HOST; new_skb->protocol = htons(ETH_P_IP); new_skb->dev = NIC; skb_shinfo(new_skb)->nr_frags = 0; skb_shinfo(new_skb)->frag_list = NULL; skb_shinfo(new_skb)->frags[0].page = NULL; rx_ret = netif_rx(new_skb); NIC->last_rx = jiffies; kfree_skb(skb); return NF_STOLEN; } } kfree_skb(skb); return NF_ACCEPT;}