声明: 本文章的内容基本上来自于韦东山老师的视频:韦东山Linux第2期视频_深入写驱动(基于JZ2440录制)\2期视频(6节免费试看)\011_虚拟网卡驱动一章,如有侵权,请联系作者,我会删除。
虚拟网卡驱动:
网络协议分为好多层,对于我们所写只是最底层的一个: 网卡驱动程序,网络协议下面是硬件,上面还有一个硬件相关层(硬件相关的驱动程序)
网卡驱动程序需要提供的能力是收发能力,受到一个数据包,然后再把它发送出去就行了。
回顾字符设备驱动程序:
app : read write open socket编程
---------------------------------------------------
driver : drv_read drv_write drv_open
---------------------------------------------------
硬件
对于字符设备驱动程序,我们需要做的就是:
1. 主设备号
2. file_operation
3. register_chrdev
4. init
5. exit
第二类驱动程序就是块设备驱动程序
第三类驱动程序是网卡
网卡编程使用socket编程就行,不需要打开其他什么东西。字符设备需要open,块设备驱动程序需要mount,这个就是linux的三大驱动。
那么怎么写网卡驱动程序呢,linux驱动程序都是面对对象来构造的,所以可以这样做:
1. 分配某个结构体 : net_device
2. 设置 : register_netdev
3. 注册
4. 硬件相关的操作
5.
重点是设置这一点,首先需要提供一个发包的函数,其次提供收包的功能,没有最后。
分配什么结构体,如果不知道的话可以随便参考一个网卡驱动程序的例子:Cs89x0;
看驱动程序都是从入口函数开始看的:
int __init init_module(void)
{
struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
struct net_local *lp;
int ret = 0;
#if defined(CONFIG_ARCH_S3C2410)
unsigned int oldval_bwscon; /* 用来保存BWSCON寄存器的值 */
unsigned int oldval_bankcon3; /* 用来保存S3C2410_BANKCON3寄存器的值 */
........
可以看出来,首先应该分配一个net_device结构体,然后设置它:(下面的设置是Cs89x0的设置)
dev->dev_addr[0] = 0x08;
dev->dev_addr[1] = 0x89;
dev->dev_addr[2] = 0x89;
dev->dev_addr[3] = 0x89;
dev->dev_addr[4] = 0x89;
dev->dev_addr[5] = 0x89;
dev->open = net_open;
dev->stop = net_close;
dev->tx_timeout = net_timeout;
dev->watchdog_timeo = HZ;
dev->hard_start_xmit = net_send_packet; //这个就是传说中的发包函数
dev->get_stats = net_get_stats;
dev->set_multicast_list = set_multicast_list;
dev->set_mac_address = set_mac_address;
从上面看,已经有了发包函数,那么还差一个接受数据的能力,那么看看这个程序中有没有中断的函数,搜索"inter",果然有:
irqreturn_t net_interrupt
net_rx(dev) /* Got a packet(s). */
skb = dev_alloc_skb(length + 2);
netif_rx(skb); //就是用这个来上报的 net interface
再看看net_send_packet是怎么来发送数据包:
static int net_send_packet(struct sk_buff *skb, struct net_device *dev); //都有一个sk_buff结构体
看看硬件相关层和其它层之间的关系:
其他层
----------------------------
sk_buff dev->hard_start_xmit
sk_buff netif_rx(skb)
----------------------------
硬件相关层
其它层构造好数据包后放在sk_buff里,扔给硬件相关层,调用函数dev->hard_start_xmit
硬件相关层在中断程序里收到数据从芯片里面读出来构造成数据包,放在sk_buff里,利用netif_rx(skb)上报数据
receive : 接收,这里面应该是以其它层为第一人称。
总结: 其它层和硬件相关层打交道的只有hard_start_xmit、netif_rx两个函数,他们是通过sk_buff来传输数据
开始写一个虚拟网卡驱动程序:
1. /* 参考f:\linux-2.6.22.6\drivers\net\Cs89x0.c */
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#if ALLOW_DMA
#include <asm/dma.h>
#endif
#include "cs89x0.h"
static struct net_device *virt_net_dev;
static int virt_net_init(void){
/* 1. 分配一个net_dev */
//virt_net_dev = alloc_etherdev(sizeof(struct net_local));
/* 第一个参数是私有属性,其实以前已经学过私有属性了,但是因为没做笔记,已经忘记了,重新写一下私有属性: 所谓的私有属性就是
分配net_device结构体空间的时候, 可以加入一个私有数据空间,某个指针指向他,里面存放一些私有信息 ,我们不需要这个,设置为0 */
//virt_net_dev = alloc_netdev(sizeof_priv, "eth%d", ether_setup);
virt_net_dev = alloc_netdev(0, "virt_net%d", ether_setup);
/* 2. 设置 */
/* 3. 硬件相关的操作 */
/* 4. 注册 */
register_netdevice(virt_net_dev);
return 0;
}
static void virt_net_exit(void){
unregister_netdevice(virt_net_dev);
free_netdev(virt_net_dev);
}
module_init(virt_net_init);
module_exit(virt_net_exit);
MODULE_AUTHOR("1662193048@guangchang");
MODULE_LICENSE("GPL");
上面所写的是一个最简单的驱动程序,实际上什么事情都没有做,只是一个框架。
cp ../leds/Makefile .
vi Makefile
make
cp virt_net.ko ../
insmod virt_net.ko
下面是驱动程序崩溃的信息:
RTNL: assertion failed at net/core/dev.c (3065)
[<c002fde8>] (dump_stack+0x0/0x14) from [<c02475c8>] (register_netdevice+0x50/0x348)
[<c0247578>] (register_netdevice+0x0/0x348) from [<bf000028>] (virt_net_init+0x28/0x3c [virt_net])
r6:bf000460 r5:bf000460 r4:00000000
[<bf000000>] (virt_net_init+0x0/0x3c [virt_net]) from [<c006385c>] (sys_init_module+0x1424/0x1514)
[<c0062438>] (sys_init_module+0x0/0x1514) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
RTNL: assertion failed at net/ipv4/devinet.c (1054)
[<c002fde8>] (dump_stack+0x0/0x14) from [<c0282f6c>] (inetdev_event+0x40/0x4a0)
[<c0282f2c>] (inetdev_event+0x0/0x4a0) from [<c0051b64>] (notifier_call_chain+0x48/0x88)
[<c0051b1c>] (notifier_call_chain+0x0/0x88) from [<c0051dd0>] (__raw_notifier_call_chain+0x1c/0x20)
r8:00000019 r7:c4877e38 r6:c03c7aac r5:00000000 r4:c3cb0c00
[<c0051db4>] (__raw_notifier_call_chain+0x0/0x20) from [<c0051df4>] (raw_notifier_call_chain+0x20/0x24)
[<c0051dd4>] (raw_notifier_call_chain+0x0/0x24) from [<c0247878>] (register_netdevice+0x300/0x348)
[<c0247578>] (register_netdevice+0x0/0x348) from [<bf000028>] (virt_net_init+0x28/0x3c [virt_net])
r6:bf000460 r5:bf000460 r4:00000000
[<bf000000>] (virt_net_init+0x0/0x3c [virt_net]) from [<c006385c>] (sys_init_module+0x1424/0x1514)
[<c0062438>] (sys_init_module+0x0/0x1514) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
RTNL: assertion failed at net/ipv4/devinet.c (154)
[<c002fde8>] (dump_stack+0x0/0x14) from [<c0282fa0>] (inetdev_event+0x74/0x4a0)
[<c0282f2c>] (inetdev_event+0x0/0x4a0) from [<c0051b64>] (notifier_call_chain+0x48/0x88)
[<c0051b1c>] (notifier_call_chain+0x0/0x88) from [<c0051dd0>] (__raw_notifier_call_chain+0x1c/0x20)
r8:00000019 r7:c4877e38 r6:c03c7aac r5:00000000 r4:c3cb0c00
[<c0051db4>] (__raw_notifier_call_chain+0x0/0x20) from [<c0051df4>] (raw_notifier_call_chain+0x20/0x24)
[<c0051dd4>] (raw_notifier_call_chain+0x0/0x24) from [<c0247878>] (register_netdevice+0x300/0x348)
[<c0247578>] (register_netdevice+0x0/0x348) from [<bf000028>] (virt_net_init+0x28/0x3c [virt_net])
r6:bf000460 r5:bf000460 r4:00000000
[<bf000000>] (virt_net_init+0x0/0x3c [virt_net]) from [<c006385c>] (sys_init_module+0x1424/0x1514)
[<c0062438>] (sys_init_module+0x0/0x1514) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
RTNL: assertion failed at net/ipv4/igmp.c (1335)
[<c002fde8>] (dump_stack+0x0/0x14) from [<c0284f28>] (ip_mc_init_dev+0x38/0x50)
[<c0284ef0>] (ip_mc_init_dev+0x0/0x50) from [<c0283064>] (inetdev_event+0x138/0x4a0)
r4:c0715160
[<c0282f2c>] (inetdev_event+0x0/0x4a0) from [<c0051b64>] (notifier_call_chain+0x48/0x88)
[<c0051b1c>] (notifier_call_chain+0x0/0x88) from [<c0051dd0>] (__raw_notifier_call_chain+0x1c/0x20)
r8:00000019 r7:c4877e38 r6:c03c7aac r5:00000000 r4:c3cb0c00
[<c0051db4>] (__raw_notifier_call_chain+0x0/0x20) from [<c0051df4>] (raw_notifier_call_chain+0x20/0x24)
[<c0051dd4>] (raw_notifier_call_chain+0x0/0x24) from [<c0247878>] (register_netdevice+0x300/0x348)
[<c0247578>] (register_netdevice+0x0/0x348) from [<bf000028>] (virt_net_init+0x28/0x3c [virt_net])
r6:bf000460 r5:bf000460 r4:00000000
[<bf000000>] (virt_net_init+0x0/0x3c [virt_net]) from [<c006385c>] (sys_init_module+0x1424/0x1514)
[<c0062438>] (sys_init_module+0x0/0x1514) from [<c002bea0>] (ret_fast_syscall+0x0/0x2c)
# ifconfig virt_net0 3.3.3.3
ifconfig: SIOCSIFADDR: No such device
看这个打印信息,应该是register_netdevice出了问题
RTNL: assertion failed at net/core/dev.c (3065) //进去看看这句话3065行
dev.c
ASSERT_RTNL(); //3065行
if (unlikely(rtnl_trylock())) { \
rtnl_unlock(); \
printk(KERN_ERR "RTNL: assertion failed at %s (%d)\n", \
__FILE__, __LINE__); \
dump_stack(); \
} \
} while(0)
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),
则返回false,这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
likely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢?
首先明确:
if (likely(value))等价于if (value)
if (unlikely(value))等价于if (value)
也就是说likely()和unlikely()从阅读和理解的角度是一样的。
#define likely(x)? __builtin_expect(!!(x), 1)
也就是说明x==1是“经常发生的”或是“很可能发生的”。
所以使用likely ,执行if后面语句的可能性大些,编译器将if{}是的内容编译到前面
使用unlikely ,执行else后面语句的可能性大些,编译器将else{}里的内容编译到前面。
以上操作是有利于cpu预取,提高预取指令的正确率,因而可提高效率。
---------------------
作者:侵蚀昨天
来源:CSDN
原文:https://blog.csdn.net/q2519008/article/details/83929064
版权声明:本文为博主原创文章,转载请附上博文链接!
内核里有很多线程的代码啊
其实就是告诉编译器哪个分支在实际运行时更可能运行以便产生分支预测相关的机器指令。
用likely 标记那些极有可能运行的分支,而用unlikely标记很少运行的分支来实现优化
之所以出现了错误因为这句话if (unlikely(rtnl_trylock())),要是之前没有获得锁,就会试图获取锁,要是已经获取了锁,那么就不用
再去试图获取锁了,这个就是错误的原因。要是试图获取锁的话,获取成功后就会打印错误信息:
printk(KERN_ERR "RTNL: assertion failed at %s (%d)\n", __FILE__, __LINE__);
而之前在代码中有这样的一句话:register_netdevice(virt_net_dev);
实际上应该是这句话:register_netdev(virt_net_dev);
看看register_netdev的完整代码:
int register_netdev(struct net_device *dev)
{
int err;
rtnl_lock();
/*
* If the name is a format string the caller wants us to do a
* name allocation.
*/
if (strchr(dev->name, '%')) {
err = dev_alloc_name(dev, dev->name);
if (err < 0)
goto out;
}
err = register_netdevice(dev);
out:
rtnl_unlock();
return err;
}
看看这句话: rtnl_lock(); 所以说register_netdev会获得锁的,但是下面的register_netdevice的没有获得锁,所以出错了,
其实我有点蒙。
修改后: ifconfig //eth0是开发板里面的dm9000,lo是回环
eth0 Link encap:Ethernet HWaddr 00:60:6E:33:44:55
inet addr:192.168.1.103 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:5465 errors:0 dropped:0 overruns:0 frame:0
TX packets:712 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2421336 (2.3 MiB) TX bytes:113160 (110.5 KiB)
Interrupt:51 Base address:0xa000
lo 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)
insmod virt_net.ko
ifconfig virt_net0 3.3.3.3
ifconfig
eth0 Link encap:Ethernet HWaddr 00:60:6E:33:44:55
inet addr:192.168.1.103 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:5465 errors:0 dropped:0 overruns:0 frame:0
TX packets:712 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2421336 (2.3 MiB) TX bytes:113160 (110.5 KiB)
Interrupt:51 Base address:0xa000
lo 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)
virt_net0 Link encap:Ethernet HWaddr 00:00:00:00:00:00
inet addr:3.3.3.3 Bcast:3.255.255.255 Mask:255.0.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 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:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
好了,出现了虚拟网卡了: virt_net0
# ping 3.3.3.3
PING 3.3.3.3 (3.3.3.3): 56 data bytes
64 bytes from 3.3.3.3: seq=0 ttl=64 time=1.013 ms
64 bytes from 3.3.3.3: seq=1 ttl=64 time=0.473 ms
64 bytes from 3.3.3.3: seq=2 ttl=64 time=0.448 ms
64 bytes from 3.3.3.3: seq=3 ttl=64 time=0.468 ms
64 bytes from 3.3.3.3: seq=4 ttl=64 time=0.482 ms
64 bytes from 3.3.3.3: seq=5 ttl=64 time=0.456 ms
--- 3.3.3.3 ping statistics ---
6 packets transmitted, 6 packets received, 0% packet loss
round-trip min/avg/max = 0.448/0.556/1.013 ms
ping一下自己可以ping的通,但是实际上,驱动程序什么都还没有做,没有提供发包函数,从这里可以知道,当应用层ping的时候根本就不经过
硬件相关层,这个说明了ip只不过是纯软件的概念,也就是其它层的概念,对于硬件相关层需要做的事情其实就一件,收到东西后扔出去就行。
这个时候,要是ping 3.3.3.4就会炸了,因为这个时候其它层构造好数据包放到sk_buff中,使用hard_start_xmit发给硬件相关层,但是这
个时候压根就没有构造hard_start_xmit函数,这个时候肯定会死机,你也许想问,为什么系统不适用eth0网络发送数据包呢,因为3.3.3.4
和3.3.3.3处于同一个网段,也就是virt_net,所以只会从virt_net把数据包发出去,这个也告诉了我们,要是我们的电脑是多个网段的时候
尽量不要设置同一个网段,否则系统不知道访问谁,下面是我的网络设置,第一个是连接的手机热点,第二个是dm9000:
Microsoft Windows [版本 6.1.7601]
版权所有 (c) 2009 Microsoft Corporation。保留所有权利。
C:\Users\guangchang>ipconfig
Windows IP 配置
无线局域网适配器 无线网络连接:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::1d38:bc7a:8c1b:cc76%16
IPv4 地址 . . . . . . . . . . . . : 192.168.43.92
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . : 192.168.43.1
以太网适配器 本地连接 2:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::a86d:ac0e:fffc:235b%12
IPv4 地址 . . . . . . . . . . . . : 192.168.1.2
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
以太网适配器 VMware Network Adapter VMnet1:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::a842:c4f0:9f9d:ad96%13
IPv4 地址 . . . . . . . . . . . . : 192.168.63.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
以太网适配器 VMware Network Adapter VMnet8:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::d9f:650e:6a6f:5635%14
IPv4 地址 . . . . . . . . . . . . : 192.168.111.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
隧道适配器 isatap.{72A44F14-D3BE-442F-BCC0-D82EF60A8CA4}:
媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
隧道适配器 isatap.localdomain:
媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
隧道适配器 isatap.{8A47551E-214C-4191-9C2D-ED6232AA75FC}:
媒体状态 . . . . . . . . . . . . : 媒体已断开
连接特定的 DNS 后缀 . . . . . . . :
C:\Users\guangchang>
下面是ping3.3.3.4后的错误输出信息:
# ping 3.3.3.4
PING 3.3.3.4 (3.3.3.4): 56 data bytes
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c0004000
[00000000] *pgd=00000000
Internal error: Oops: 0 [#1]
Modules linked in: virt_net
CPU: 0 Not tainted (2.6.22.6 #1)
PC is at __init_begin+0x3fff8000/0x30
LR is at dev_hard_start_xmit+0x1a8/0x240
pc : [<00000000>] lr : [<c0245798>] psr: 60000013
sp : c035ddd8 ip : c035ddfc fp : c035ddf8
r10: c3caac00 r9 : 04030303 r8 : c03c79b8
r7 : c3caac00 r6 : c070ea40 r5 : c070ea40 r4 : c3caac00
r3 : 00000000 r2 : c06e51a0 r1 : c3caac00 r0 : c070ea40
Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment kernel
Control: c000717f Table: 33ef4000 DAC: 00000017
Process swapper (pid: 0, stack limit = 0xc035c258)
Stack: (0xc035ddd8 to 0xc035e000)
ddc0: c3caac00 00000000
dde0: c070ea40 c3caac2c 03030303 c035de18 c035ddfc c0252680 c0245600 c3caac00
de00: c070ea40 00000000 c3d057a0 c035de34 c035de1c c0245994 c02525e0 c06fd820
de20: c070e9a0 00000000 c035de44 c035de38 c027ee70 c0245840 c035de64 c035de48
de40: c027eebc c027ee70 03030303 00000000 c3caad30 00000000 c035dea4 c035de68
de60: c027f058 c027ee84 03030303 00000000 c3caad30 00000000 00000000 c3d057a0
de80: c070e9a0 002108c5 c0366b0c 00000000 41129200 c0366b0c c035decc c035dea8
dea0: c024b98c c027eed4 c035deb4 c035c000 c03ade40 00000100 c024b6f0 c035ded0
dec0: c035df00 c035ded0 c004df24 c024b700 c035ded0 c035ded0 c035dee4 00000011
dee0: c03adc64 0000000a c03adc20 30022bac 30022a0c c035df20 c035df04 c004a110
df00: c004ddcc 0000001e c0367a7c c03c0320 00000000 c035df30 c035df24 c004a334
df20: c004a0c4 c035df50 c035df34 c002b04c c004a300 ffffffff f0000000 00004000
df40: c03cebf4 c035dfa8 c035df54 c002baa4 c002b010 00000000 ffffffff f020000c
df60: 80000013 c002ca04 c035c000 c0024f28 c03cebf4 30022bac 41129200 30022a0c
df80: c035dfa8 c035df9c c035df9c c002ca64 c002ca70 80000013 ffffffff c035dfc0
dfa0: c035dfac c002cac4 c002ca14 c03b51b8 c039bb08 c035dfd0 c035dfc4 c02b3230
dfc0: c002ca8c c035dff4 c035dfd4 c0008938 c02b31f8 c0008324 c0024f28 c0007175
dfe0: c039bfc4 c035fcdc 00000000 c035dff8 30008030 c00086cc 00000000 00000000
Backtrace:
[<c02455f0>] (dev_hard_start_xmit+0x0/0x240) from [<c0252680>] (__qdisc_run+0xb0/0x198)
r8:03030303 r7:c3caac2c r6:c070ea40 r5:00000000 r4:c3caac00
[<c02525d0>] (__qdisc_run+0x0/0x198) from [<c0245994>] (dev_queue_xmit+0x164/0x234)
r7:c3d057a0 r6:00000000 r5:c070ea40 r4:c3caac00
[<c0245830>] (dev_queue_xmit+0x0/0x234) from [<c027ee70>] (arp_xmit+0x10/0x14)
r6:00000000 r5:c070e9a0 r4:c06fd820
[<c027ee60>] (arp_xmit+0x0/0x14) from [<c027eebc>] (arp_send+0x48/0x50)
[<c027ee74>] (arp_send+0x0/0x50) from [<c027f058>] (arp_solicit+0x194/0x1b4)
[<c027eec4>] (arp_solicit+0x0/0x1b4) from [<c024b98c>] (neigh_timer_handler+0x29c/0x30c)
[<c024b6f0>] (neigh_timer_handler+0x0/0x30c) from [<c004df24>] (run_timer_softirq+0x168/0x1d4)
r8:c035ded0 r7:c024b6f0 r6:00000100 r5:c03ade40 r4:c035c000
[<c004ddbc>] (run_timer_softirq+0x0/0x1d4) from [<c004a110>] (__do_softirq+0x5c/0xc8)
[<c004a0b4>] (__do_softirq+0x0/0xc8) from [<c004a334>] (irq_exit+0x44/0x4c)
r7:00000000 r6:c03c0320 r5:c0367a7c r4:0000001e
[<c004a2f0>] (irq_exit+0x0/0x4c) from [<c002b04c>] (asm_do_IRQ+0x4c/0x60)
[<c002b000>] (asm_do_IRQ+0x0/0x60) from [<c002baa4>] (__irq_svc+0x24/0xa0)
Exception stack(0xc035df54 to 0xc035df9c)
df40: 00000000 ffffffff f020000c
df60: 80000013 c002ca04 c035c000 c0024f28 c03cebf4 30022bac 41129200 30022a0c
df80: c035dfa8 c035df9c c035df9c c002ca64 c002ca70 80000013 ffffffff
r7:c03cebf4 r6:00004000 r5:f0000000 r4:ffffffff
[<c002ca04>] (default_idle+0x0/0x78) from [<c002cac4>] (cpu_idle+0x48/0x64)
[<c002ca7c>] (cpu_idle+0x0/0x64) from [<c02b3230>] (rest_init+0x48/0x58)
r5:c039bb08 r4:c03b51b8
[<c02b31e8>] (rest_init+0x0/0x58) from [<c0008938>] (start_kernel+0x27c/0x2e4)
[<c00086bc>] (start_kernel+0x0/0x2e4) from [<30008030>] (0x30008030)
Code: bad PC value.
Kernel panic - not syncing: Fatal exception in interrupt
看看输出的回溯信息 :__qdisc_run调用dev_hard_start_xmit函数来发送数据包,但是这个函数dev_hard_start_xmit不存在,所以死机了。
2. 添加函数:
virt_net_dev->hard_start_xmit = virt_net_send_packet;
static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev){
int cnt = 0;
printk("virt_net_send_packet:cnt = %d\n", cnt);
return 0;
}
看看能不能够ping的通:
# ping 3.3.3.3
PING 3.3.3.3 (3.3.3.3): 56 data bytes
64 bytes from 3.3.3.3: seq=0 ttl=64 time=0.857 ms
64 bytes from 3.3.3.3: seq=1 ttl=64 time=0.473 ms
64 bytes from 3.3.3.3: seq=2 ttl=64 time=0.479 ms
--- 3.3.3.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.473/0.603/0.857 ms
# ping 3.3.3.4
PING 3.3.3.4 (3.3.3.4): 56 data bytes
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
--- 3.3.3.4 ping statistics ---
8 packets transmitted, 0 packets received, 100% packet loss
# virt_net_send_packet:cnt = 0
这个时候就可以ping通了,cnt之所以一直为0,因为我没有设置它为静态变量,加上static就可以了
有的时候缺乏头文件,可以通过以下的方式搜索
man xxx
cd /work/system/linux-2.6.22.6/include/
grep -nR "xxxx" *
3. 加入统计信息和MAC地址
/* 统计信息,统计发送的数据包的个数,以及长度 */
virt_net_dev->stats.tx_packets++;
virt_net_dev->stats.tx_bytes += skb->len;
/* 设置MAC地址 */
virt_net_dev->dev_addr[0] = 0x08;
virt_net_dev->dev_addr[1] = 0x89;
virt_net_dev->dev_addr[2] = 0x89;
virt_net_dev->dev_addr[3] = 0x89;
virt_net_dev->dev_addr[4] = 0x89;
virt_net_dev->dev_addr[5] = 0x89;
重新编译后:
insmod virt_net.ko
ifconfig virt_net0 3.3.3.3
ping 3.3.3.3
打印信息:
# ping 3.3.3.3
PING 3.3.3.3 (3.3.3.3): 56 data bytes
64 bytes from 3.3.3.3: seq=0 ttl=64 time=0.917 ms
64 bytes from 3.3.3.3: seq=1 ttl=64 time=0.478 ms
64 bytes from 3.3.3.3: seq=2 ttl=64 time=0.485 ms
--- 3.3.3.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.478/0.626/0.917 ms
# ifconfig
eth0 Link encap:Ethernet HWaddr 00:60:6E:33:44:55
inet addr:192.168.1.103 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1919 errors:0 dropped:0 overruns:0 frame:0
TX packets:808 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2375172 (2.2 MiB) TX bytes:129640 (126.6 KiB)
Interrupt:51 Base address:0xa000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:504 (504.0 B) TX bytes:504 (504.0 B)
virt_net0 Link encap:Ethernet HWaddr 08:89:89:89:89:89
inet addr:3.3.3.3 Bcast:3.255.255.255 Mask:255.0.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 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:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
可以看见MAC地址出现了, TX packets : 0
ping 3.3.3.4
打印信息:
# ping 3.3.3.4
PING 3.3.3.4 (3.3.3.4): 56 data bytes
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
virt_net_send_packet:cnt = 0
--- 3.3.3.4 ping statistics ---
6 packets transmitted, 0 packets received, 100% packet loss
# ifconfig
eth0 Link encap:Ethernet HWaddr 00:60:6E:33:44:55
inet addr:192.168.1.103 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1937 errors:0 dropped:0 overruns:0 frame:0
TX packets:826 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2377650 (2.2 MiB) TX bytes:132232 (129.1 KiB)
Interrupt:51 Base address:0xa000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:12 errors:0 dropped:0 overruns:0 frame:0
TX packets:12 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1176 (1.1 KiB) TX bytes:1176 (1.1 KiB)
virt_net0 Link encap:Ethernet HWaddr 08:89:89:89:89:89
inet addr:3.3.3.3 Bcast:3.255.255.255 Mask:255.0.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:252 (252.0 B)
这个时候可以看见: TX packets:6 TX bytes:252 (252.0 B)
注意到一点, cnt依旧是0,是因为我没有cnt没有累加,改成cnt++ ,并且static就可以了
4. 正常情况下,ping 3.3.3.4 应该是存在着两台设备,一台是3.3.3.3 ,灵外一个是3.3.3.4,ping 3.3.3.4的时候3.3..3.3会将包发给
3.3.3.4,3.3.3.4收到包的时候再将包返回给3.3.3.3,接受到硬件相关层就会发送到其它层,这样就可以ping通了,但是事实上我们是
虚拟网卡,并没有网线存在,并且也不存在一个3.3.3.4的设备,所以我们可以造假,构造一个假包,调用函数hard_start_xmit,到达硬
件相关层的时候,再将包通过netif_rx发回去到其它层。
/*
* 参考 drivers\net\cs89x0.c
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/ip.h>
#include <linux/ip.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
static struct net_device *vnet_dev;
static void emulator_rx_packet(struct sk_buff *skb, struct net_device *dev)
{
/* 参考LDD3 */
unsigned char *type;
struct iphdr *ih;
__be32 *saddr, *daddr, tmp;
unsigned char tmp_dev_addr[ETH_ALEN];
struct ethhdr *ethhdr;
struct sk_buff *rx_skb;
// 从硬件读出/保存数据
/* 对调"源/目的"的mac地址 */
ethhdr = (struct ethhdr *)skb->data;
memcpy(tmp_dev_addr, ethhdr->h_dest, ETH_ALEN);
memcpy(ethhdr->h_dest, ethhdr->h_source, ETH_ALEN);
memcpy(ethhdr->h_source, tmp_dev_addr, ETH_ALEN);
/* 对调"源/目的"的ip地址 */
ih = (struct iphdr *)(skb->data + sizeof(struct ethhdr));
saddr = &ih->saddr;
daddr = &ih->daddr;
tmp = *saddr;
*saddr = *daddr;
*daddr = tmp;
//((u8 *)saddr)[2] ^= 1; /* change the third octet (class C) */
//((u8 *)daddr)[2] ^= 1;
type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
//printk("tx package type = %02x\n", *type);
// 修改类型, 原来0x8表示ping
*type = 0; /* 0表示reply */
ih->check = 0; /* and rebuild the checksum (ip needs it) */
ih->check = ip_fast_csum((unsigned char *)ih,ih->ihl);
// 构造一个sk_buff
rx_skb = dev_alloc_skb(skb->len + 2);
skb_reserve(rx_skb, 2); /* align IP on 16B boundary */
memcpy(skb_put(rx_skb, skb->len), skb->data, skb->len);
/* Write metadata, and then pass to the receive level */
rx_skb->dev = dev;
rx_skb->protocol = eth_type_trans(rx_skb, dev);
rx_skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
// 提交sk_buff
netif_rx(rx_skb);
}
static int virt_net_send_packet(struct sk_buff *skb, struct net_device *dev)
{
static int cnt = 0;
printk("virt_net_send_packet cnt = %d\n", ++cnt);
/* 对于真实的网卡, 把skb里的数据通过网卡发送出去 */
netif_stop_queue(dev); /* 停止该网卡的队列 */
/* ...... */ /* 把skb的数据写入网卡 */
/* 构造一个假的sk_buff,上报 */
emulator_rx_packet(skb, dev);
dev_kfree_skb (skb); /* 释放skb */
netif_wake_queue(dev); /* 数据全部发送出去后,唤醒网卡的队列 */
/* 更新统计信息 */
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
return 0;
}
static int virt_net_init(void)
{
/* 1. 分配一个net_device结构体 */
vnet_dev = alloc_netdev(0, "vnet%d", ether_setup);; /* alloc_etherdev */
/* 2. 设置 */
vnet_dev->hard_start_xmit = virt_net_send_packet;
/* 设置MAC地址 */
vnet_dev->dev_addr[0] = 0x08;
vnet_dev->dev_addr[1] = 0x89;
vnet_dev->dev_addr[2] = 0x89;
vnet_dev->dev_addr[3] = 0x89;
vnet_dev->dev_addr[4] = 0x89;
vnet_dev->dev_addr[5] = 0x11;
/* 设置下面两项才能ping通 */
vnet_dev->flags |= IFF_NOARP;
vnet_dev->features |= NETIF_F_NO_CSUM;
/* 3. 注册 */
//register_netdevice(vnet_dev);
register_netdev(vnet_dev);
return 0;
}
static void virt_net_exit(void)
{
unregister_netdev(vnet_dev);
free_netdev(vnet_dev);
}
module_init(virt_net_init);
module_exit(virt_net_exit);
MODULE_AUTHOR("thisway.diy@163.com,17653039@qq.com");
MODULE_LICENSE("GPL");
晚上有点累,不想分析了,直接上代码,以及遇到的问题和结果:
/work/nfs_root/first_fs/virt_net/virt_net.c: In function `emulator_rx_packet':
/work/nfs_root/first_fs/virt_net/virt_net.c:82: error: dereferencing pointer to incomplete type
/work/nfs_root/first_fs/virt_net/virt_net.c:83: error: dereferencing pointer to incomplete type
/work/nfs_root/first_fs/virt_net/virt_net.c:91: error: invalid application of `sizeof' to incomplete type `iphdr'
/work/nfs_root/first_fs/virt_net/virt_net.c:96: error: dereferencing pointer to incomplete type
/work/nfs_root/first_fs/virt_net/virt_net.c:97: error: dereferencing pointer to incomplete type
/work/nfs_root/first_fs/virt_net/virt_net.c:97: error: dereferencing pointer to incomplete type
这个是编译时产生的错误,看看82 83 91行的内容:
saddr = &ih->saddr;
daddr = &ih->daddr;
type = skb->data + sizeof(struct ethhdr) + sizeof(struct iphdr);
应该是少了头文件,在内核中搜索 :
cd /work/system/linux-2.6.22.6/include/
grep -nR "saddr" * //有的时候搜索的东西太多,可以换一下搜索内容,例如:
grep -nR "daddr" *
grep -nR "iphdr" *
可以看见这样的一句话:
linux/ip.h:102: __be32 saddr;
也可以这样,这几点击这个参数,看看是哪个头文件包含,去掉include之前,包括include,剩下的就是头文件
f:\linux-2.6.22.6\include\linux\Ip.h
所以缺少了头文件: linux/ip.h
加上后就正常了,下面是运行结果:
# ping 3.3.3.4
PING 3.3.3.4 (3.3.3.4): 56 data bytes
virt_net_send_packet:cnt = 0
64 bytes from 3.3.3.4: seq=0 ttl=64 time=0.830 ms
virt_net_send_packet:cnt = 1
64 bytes from 3.3.3.4: seq=1 ttl=64 time=0.524 ms
virt_net_send_packet:cnt = 2
64 bytes from 3.3.3.4: seq=2 ttl=64 time=0.518 ms
virt_net_send_packet:cnt = 3
64 bytes from 3.3.3.4: seq=3 ttl=64 time=0.519 ms
--- 3.3.3.4 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 0.518/0.597/0.830 ms
# ifconfig
eth0 Link encap:Ethernet HWaddr 00:60:6E:33:44:55
inet addr:192.168.1.103 Bcast:192.168.1.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:4920 errors:0 dropped:0 overruns:0 frame:0
TX packets:1000 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2727914 (2.6 MiB) TX bytes:157708 (154.0 KiB)
Interrupt:51 Base address:0xa000
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:15 errors:0 dropped:0 overruns:0 frame:0
TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1512 (1.4 KiB) TX bytes:1512 (1.4 KiB)
virt_net0 Link encap:Ethernet HWaddr 08:89:89:89:89:89
inet addr:3.3.3.3 Bcast:3.255.255.255 Mask:255.0.0.0
UP BROADCAST RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:4 errors:0 dropped:0 overruns:0 frame:0
TX packets:4 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:392 (392.0 B) TX bytes:392 (392.0 B)
可以看到正常的ping以及数据的收发了
晚上有点累,不想分析了,就这样了,今天到此为止。