修改数据帧头的问题

时间:2021-01-06 07:26:25
您好,我刚刚接触Linux网络驱动方面,对驱动的各部分有了总体认识,但是有些细节不是很了解。 
目前有以下几个问题: 
1.看到网卡驱动中有skb buffer和ring buffer两种缓冲区。请问两个有什么区别,分别存放什么, 
  这两个缓冲区分别是在哪里申请的阿,比如说skb是在内存的物理映射区,那ring buffer呢? 
2.TX_ring_size和RX_ring_size以及MIN_MTU和MAX_MTU的值是怎么确定的?一个数据包的大小不是 
  46-1500字节吗?还有skb大小怎么定?缓冲区里可以容纳多少个数据包一次? 
3.netif_rx将数据包传给上层协议,这个过程是怎么进行的阿?是需要传给一个系统接口然后再传给 
  应用程序吗?那能不能让netif_rx直接把数据包传给应用程序阿?我们有个应用想通过驱动直接获取 
  并处理数据包,而不经过系统接口。 
4.在网卡驱动中,我想在数据发送前将目的MAC地址修改成想要的地址,这样的做法可行吗?即: 
  在hard_start_xmit函数的开始处添加代码: memcpy(eth->h_dest, change->daddr, dev->addr_len); 
  其中change_addr是我自己定义的。 
5.在数据接收时我想提前修改目的MAC地址和源MAC地址,那么我在net_rx函数的开头添加代码:  
  memcpy(eth->h_source, change->saddr, dev->addr_len); 
  memcpy(eth->h_dest, change->daddr, dev->addr_len); 
  这样能实现吗,可行吗?

9 个解决方案

#1


哈,点名提问呀,本人交斗胆先回答了

1 sk_buff是Linux内核中维护数据包数据用的,它的使用贯穿整个协议栈,sk_buff在接收到数据包的时候会被创建.而你说的ring buffer责是驱动自己维护的一个接收或者发送数据的缓冲,这些空间一般是在驱动启动的时候分配的.

2 TX/RX_ring_size是驱动定义的东西,它根据自己硬件特性定义的,在协议栈中并没有MIN_MTU和MAX_MTU的概念,只有MTU的概念,个人猜测它应该是这个设备的MTU可以被设置的一个范围吧.sk_buff的大小无论在接收的时候还是发送的时候都是知道数据包的大小的,只有这个2个时候创建sk_buff.
 不知道你说的这个缓冲区是指的哪个的缓冲区,一般来说网络设备的队列是1024,不过可以在驱动中设定,这个缓冲是问题3中要问答的

3 netif_rx会把sk_buff放入一个缓冲队列中,然后发出一个软中断,这个软中断用于处理sk_buff,这时就是进入了我们的协议栈了
  可以,不过不建议这么作,如果你需要可以注册一个对协议ETH_P_ALL的处理函数,那么你的这个函数就可以处理所有的数据包,至于怎么写,这个说了就太多了
  不通过系统接口,这不是个好主意,只能这个提议,因为不知道你们具体的需求.

4 可以,只是兼容性问题,如果换了别的网卡呢?
5 net_rx是什么?我暂且把它当作是你要打netif_rx吧,这个也是可以的
  4和5合起来说吧,只要确定好eth就没问题.

希望不是太难看懂

#2


建议在软中断后的函数内改;
4:在dev_queue_xmit后改更合适,需要考虑arp报文问题.
5:很别扭,做实验就罢了.

#3


看你的意思,可能想做一个二层的协议吧,你如果想将二层报文直接送到用户空间,可以尝试使用
linux的SOCK_PACKET或者Raw Socket。
SOCK_PACKET is linux specific way of getting packets at the dev level. 

环形队列是用在“轮询”收包技术中的,是一个包含sk_buff的双向循环列表,是用于DMA内存映射的。
1、首先,内核在主内存中为收发数据建立一个环形的缓冲队列(通常叫DMA环形缓冲区)。
2、内核将这个缓冲区通过DMA映射,把这个队列交给网卡;
3、网卡收到数据,就直接放进这个环形缓冲区了——也就是直接放进主内存了;然后,向系统产生一个中断;
4、内核收到这个中断,就取消DMA映射,这样,内核就直接从主内存中读取数据;[/color]
上述过程代码可以参考函数e100_rx_alloc_list与e100_rx_clean。

至于"中断"收包方式,中断收到skbuff的报文,netif_rx把报文入到队列input_pkt_queue中,然后又调用netif_rx_schedule把伪设备backlog_dev放入poll list队列,并触发软中断NET_RX_SOFTIRQ。软中断调用伪设备的poll函数process_backlog,process_backlog函数把报文出队列input_pkt_queue。交给上层(比如IP、ARP)处理。

所以如果想在收包以后修改报文的SMAC和DMAC,建议在netif_receive_skb函数中做修改。
如果想在发包的时候修改DMAC,建议在dev_queue_xmit函数中做修改。
因为这两个函数都是和网卡硬件无关的。再往下就和硬件相关了。

#4


刚才看了一下代码,SOCK_PACKET类型的Socket应该确实可以满足你的需求。
创建Socket的时候,
family = PF_PACKET
type == SOCK_PACKET
protocol自己定义。

packet_create()会直接调用dev_add_pack注册一个Packet_type,其回调函数为packet_rcv_spkt(),此
函数会将报文直接放入socket的receive_queue,实现还是挺有意思的。


#5


3.传递给应用程序,可不可以这样做呢?先通过读取PP_RxLength获取数据长度,然后读取到一个由kmalloc申请的缓冲区中,接着使用copy_to_user()拷贝到用户区。

1楼的,他那个net_rx可能是一个数据接收子程序吧,中断的时候调用的,里面使用了netif_rx(skb);

#6


呵呵,这两天网络不通没有上网,没想到这么多的高手给我指教,谢谢了!
看完大家的指点,我还有几点不明白:
1. 这个环形缓冲区是属于内存呢还是网卡的缓冲区?他被分配的空间位置和skb_buffer 的位置有什么不同?
2. 注册一个对协议ETH_P_ALL的处理函数:是指在这个协议里可以截获数据包吗?我对这个不熟悉,呵呵,不知道怎么实现。
3.一楼的前辈,我说的net_rx是驱动中的接收子程序,比如e100_rx(),由这个函数将数据包传送给netif_rx(skb)。
4.二楼和三楼的前辈都建议我在netif_receive_skb函数、dev_queue_xmit函数中做MAC地址修改。那么,这两个函数都属于Linux内核范围了吧,是针对内核进行修改了吧,能不能直接在驱动中修改呢?
5.我自己在网卡驱动中添加的memcpy(eth->h_source, change->saddr, dev->addr_len); 
  memcpy(eth->h_dest, change->daddr, dev->addr_len); 有个问题就是saddr,daddr,eth->h_source,eth->h_dest在驱动里没有定义,是在内核的别的文件定义的,这样的程序行得通吗?
6.二楼的前辈,有SOCK_PACKET方面的学习和应用资料吗,我学习下。
7.五楼的前辈,你的方法可以实现吗,怎么实现,我们原计划也是希望这样实现。
拜谢各位!

#7


汗,不好意思楼主,没有了解网络这块...

#8


哦,呵呵,没关系,那就请其他的高手前辈赐教了。

#9


2 楼的说的清楚

#1


哈,点名提问呀,本人交斗胆先回答了

1 sk_buff是Linux内核中维护数据包数据用的,它的使用贯穿整个协议栈,sk_buff在接收到数据包的时候会被创建.而你说的ring buffer责是驱动自己维护的一个接收或者发送数据的缓冲,这些空间一般是在驱动启动的时候分配的.

2 TX/RX_ring_size是驱动定义的东西,它根据自己硬件特性定义的,在协议栈中并没有MIN_MTU和MAX_MTU的概念,只有MTU的概念,个人猜测它应该是这个设备的MTU可以被设置的一个范围吧.sk_buff的大小无论在接收的时候还是发送的时候都是知道数据包的大小的,只有这个2个时候创建sk_buff.
 不知道你说的这个缓冲区是指的哪个的缓冲区,一般来说网络设备的队列是1024,不过可以在驱动中设定,这个缓冲是问题3中要问答的

3 netif_rx会把sk_buff放入一个缓冲队列中,然后发出一个软中断,这个软中断用于处理sk_buff,这时就是进入了我们的协议栈了
  可以,不过不建议这么作,如果你需要可以注册一个对协议ETH_P_ALL的处理函数,那么你的这个函数就可以处理所有的数据包,至于怎么写,这个说了就太多了
  不通过系统接口,这不是个好主意,只能这个提议,因为不知道你们具体的需求.

4 可以,只是兼容性问题,如果换了别的网卡呢?
5 net_rx是什么?我暂且把它当作是你要打netif_rx吧,这个也是可以的
  4和5合起来说吧,只要确定好eth就没问题.

希望不是太难看懂

#2


建议在软中断后的函数内改;
4:在dev_queue_xmit后改更合适,需要考虑arp报文问题.
5:很别扭,做实验就罢了.

#3


看你的意思,可能想做一个二层的协议吧,你如果想将二层报文直接送到用户空间,可以尝试使用
linux的SOCK_PACKET或者Raw Socket。
SOCK_PACKET is linux specific way of getting packets at the dev level. 

环形队列是用在“轮询”收包技术中的,是一个包含sk_buff的双向循环列表,是用于DMA内存映射的。
1、首先,内核在主内存中为收发数据建立一个环形的缓冲队列(通常叫DMA环形缓冲区)。
2、内核将这个缓冲区通过DMA映射,把这个队列交给网卡;
3、网卡收到数据,就直接放进这个环形缓冲区了——也就是直接放进主内存了;然后,向系统产生一个中断;
4、内核收到这个中断,就取消DMA映射,这样,内核就直接从主内存中读取数据;[/color]
上述过程代码可以参考函数e100_rx_alloc_list与e100_rx_clean。

至于"中断"收包方式,中断收到skbuff的报文,netif_rx把报文入到队列input_pkt_queue中,然后又调用netif_rx_schedule把伪设备backlog_dev放入poll list队列,并触发软中断NET_RX_SOFTIRQ。软中断调用伪设备的poll函数process_backlog,process_backlog函数把报文出队列input_pkt_queue。交给上层(比如IP、ARP)处理。

所以如果想在收包以后修改报文的SMAC和DMAC,建议在netif_receive_skb函数中做修改。
如果想在发包的时候修改DMAC,建议在dev_queue_xmit函数中做修改。
因为这两个函数都是和网卡硬件无关的。再往下就和硬件相关了。

#4


刚才看了一下代码,SOCK_PACKET类型的Socket应该确实可以满足你的需求。
创建Socket的时候,
family = PF_PACKET
type == SOCK_PACKET
protocol自己定义。

packet_create()会直接调用dev_add_pack注册一个Packet_type,其回调函数为packet_rcv_spkt(),此
函数会将报文直接放入socket的receive_queue,实现还是挺有意思的。


#5


3.传递给应用程序,可不可以这样做呢?先通过读取PP_RxLength获取数据长度,然后读取到一个由kmalloc申请的缓冲区中,接着使用copy_to_user()拷贝到用户区。

1楼的,他那个net_rx可能是一个数据接收子程序吧,中断的时候调用的,里面使用了netif_rx(skb);

#6


呵呵,这两天网络不通没有上网,没想到这么多的高手给我指教,谢谢了!
看完大家的指点,我还有几点不明白:
1. 这个环形缓冲区是属于内存呢还是网卡的缓冲区?他被分配的空间位置和skb_buffer 的位置有什么不同?
2. 注册一个对协议ETH_P_ALL的处理函数:是指在这个协议里可以截获数据包吗?我对这个不熟悉,呵呵,不知道怎么实现。
3.一楼的前辈,我说的net_rx是驱动中的接收子程序,比如e100_rx(),由这个函数将数据包传送给netif_rx(skb)。
4.二楼和三楼的前辈都建议我在netif_receive_skb函数、dev_queue_xmit函数中做MAC地址修改。那么,这两个函数都属于Linux内核范围了吧,是针对内核进行修改了吧,能不能直接在驱动中修改呢?
5.我自己在网卡驱动中添加的memcpy(eth->h_source, change->saddr, dev->addr_len); 
  memcpy(eth->h_dest, change->daddr, dev->addr_len); 有个问题就是saddr,daddr,eth->h_source,eth->h_dest在驱动里没有定义,是在内核的别的文件定义的,这样的程序行得通吗?
6.二楼的前辈,有SOCK_PACKET方面的学习和应用资料吗,我学习下。
7.五楼的前辈,你的方法可以实现吗,怎么实现,我们原计划也是希望这样实现。
拜谢各位!

#7


汗,不好意思楼主,没有了解网络这块...

#8


哦,呵呵,没关系,那就请其他的高手前辈赐教了。

#9


2 楼的说的清楚