驱动程序实际是内核和网卡之间的桥梁,网卡内部的程序属于嵌入式一部分,内部也是非常的复杂。驱动程序本质上是个中断处理程序,当网卡触发中断后,由这个驱动程序根据内核的接口规范,将数据通过内核接口传送给内核。当内核有数据要发送时,也是通过驱动程序将数据发送给网卡。因此,驱动程序本质上属于内核的一部分。
在linux中,表明一块网卡的数据结构是net_device,每一个net_device结构代表一块网卡。因此,我们若要虚拟一块网卡,需要通过构造一个net_device结构来实现。
1. 在snull_init_module里面,虚拟构造了两块网卡,并调用了register_netdev这个内核方法,将两块虚拟网卡注册到了内核,这样通过ifconfig等命令就可以看到这些网卡信息。
2. 在调用register_netdev这个方法里,会自动去调用net_device里面的snull_init方法,该方法是对net_device做初始化。
3. snull_init里面,主要是设置设备open、stop、hard_start_xmit、do_ioctl等等内核接口的函数地址,因为内核是通过net_device这些接口来控制这个网卡的各种行为的。
4. 在snull程序里面,在net_device里面有一个指针,是指向驱动程序的私有数据结构。这个结构就是snull_priv,里面有个链表,串了一些snull_packet结构
5. 当内核要发送一个数据包的时候,是调用net_device里面的hard_start_xmit函数的,在这里这个函数的地址就是snull_tx;这个函数最终是调用了snull_hw_tx将包交给网卡的;
6. 在snull_hw_tx函数里,首先通过计算以太网的头部后取到了ip头部,拿到了source ip和dst ip,然后直接做了修改。比如dst:192.168.0.2 src:192.168.0.1,变成了dst:192.168.1.2 src:192.168.1.1,从底层来看从sn0发出去的包,一下子又从sn1回来了。紧接着,需要对sn1所对应的net_device模仿一次中断,好像真的物理网卡收到包一样。这个数据放到了net_device的private数据结构里面的snull_packet队列里面。然后将private里面的状态置为了收到包中断,然后直接调用了snull_interrupt函数,传入的是dst的net_device结构。
7. 在没有采用napi模式下,采用的中断函数为snull_regular_interrupt。这个函数就干1件事:判断一下,若是接受中断,则直接将收到的那个包取出来,调用snull_rx函数。若是发送中断,则在net_device的private结构里面增加一些statistics信息,然后就释放skb结构了
8. snull_rx函数的功能就更加简单,根据收到的packet构造一个skb结构,然后调用netif_rx这个内核函数,其任务就完成了。
9. 通过上述分析,我们看到在虚拟网卡层,一个从虚拟网卡发送出去的包,又神奇的被另外一个虚拟网卡给接受到了。
sudo ifconfig sn0 192.168.0.1
sudo ifconfig sn1 192.168.1.2
ping 192.168.0.2
ping 192.168.1.1
sudo ifconfig sn0 down
sudo ifconfig sn1 down
阅读(1126) | 评论(0) | 转发(0) |