Chinaunix首页 | 论坛 | 博客
  • 博客访问: 529881
  • 博文数量: 120
  • 博客积分: 3030
  • 博客等级: 中校
  • 技术积分: 1445
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-05 01:00
文章存档

2011年(1)

2009年(2)

2008年(32)

2007年(33)

2006年(52)

我的朋友

分类: LINUX

2006-04-01 23:50:09

Linux 2.4 中网络包在网络协议栈中的旅程

翻译自 Harald Welte

 

2.4 内核由于采用了softirq 机制,因此网络包在内核中旅程与2.2 内核比起来有了很大的不同,所以我们需要重新讲解一下:),关于softirq/tasklet/bottom half 的讲解参看我画的图。

 

1.         网络包的接收

a)         接收中断

如果网卡探测到一个以太网帧的目的MAC 地址与本网卡的地址匹配的话,或者此帧是链路层的广播包的话,网卡就会接收此帧,放到网卡自己的缓冲区中,然后向cpu 发出一个中断请求,cpu 响应此中断请求,调用此网卡相应的中断处理程序,在中断处理程序中,把此包从网卡的缓冲区中拷贝到内存中,方式可以是DMA/PIO 等等。然后会分配一个skb 结构,最后会呼叫与协议没有关系的netif_rx() 函数(net/core/dev.c:netif_rx(skb) ),此函数主要负责来给此包打上时间戳标记,然后把此包对应的skb 放到对应的cpu 的接收队列中,但是如果接收队列以满的话,此包会被丢弃。Netif_rx 函数最后给接收网络包的软中断(softirq) 打上标记:__cpu_raise_softirq()(include/linux/interrupt.h) 。至此硬件对应的中断处理程序就完成了,即网络包的前期处理完成了,而其后期处理交给了软中断RX softirq

b)        RX softirq

这部分是与2.2 内核完全不一样的,2.2 内核中使用bottom half 机制来处理,而在2.4 内核中使用的是softirq 机制。Softirq 机制与bottom half 机制相比,优点是可以在多个cpu 上同时处理,但对于bottom half ,会保证任意时刻只会在一个cpu 上运行。

RX softirq 的注册是在net/core/dev/c:net_init() 函数中,使用kernel/softirq.c:open_softirq() 来注册的。

网络包的后期处理是在NET_RX_SOFTIRQ 中完成的。它是在kernel/softirq.c:do_softirq() 中被呼叫的。而do_softirq ()在三种时机会被调用:

1.         arch/i386/kernel/irq.c:do_IRQ() ,这个是IRQ 处理(硬件IRQ )函数。

2.         arch/i386/kernel/entry.S 内核从系统调用返回。

3.         在进程调度函数中:kernel/sched.c:schedule()

所以,如果cpu 执行到以上三种时刻时,会呼叫do_softirq() 函数。在此函数中,如果检测到NET_RX_SOFTIRQ 被打上标记,就会执行net/core/dev.c:net_rx_action() 。在net_rx_action() 函数中,skb 会被从相应cpu 的接收队列中取出,根据skb 的类型,调用相应的处理函数,对于ip 包来说,就是ip_rcv() 函数。

c)        ip 包的处理函数是通过net/core/dev.c:dev_add_pack() 函数注册的。而后者是在net/ipv4/ip_output.c:ip_init() 函数中调用的,ip_init() 用于初始化各种结构,注册ip 包的处理函数。对于ipv4 来说,包处理函数是net/ipv4/ip_input.c:ip_rcv() ,此函数首先会做一些校验,如ip 头的校验,包长的校验,ip 协议版本号的校验等。如果以上的校验出错的话,此ip 包会被丢弃。然后会计算ip 包的长度,以及去处传输层可能加的一些没有用的padding 。此后,第一个NETFILTER 钩子会被调用 。当执行完netfilter 钩子函数后,会呼叫/net/ipv4/ipv_input.cipv_rcv_finish() 。在ipv_rcv_finish 函数中,此包的目的地址会由net/ipv4/route.c:ip_route_input() 计算出来。此外如果我们的ip 包包含ip 选项的话,也会在此处理。根据net/ipv4/route.c:ip_route_input_slow() ,我们的ip 包会有两条不同的路走:一条是针对此ip 包是发给本机节点的,一条是针对此ip 包是需要转发的。

1.         net/ipv4/ip_input.c:ip_local_deliver() ip 包是发给自己的,内核应该把此包交给上层协议。

2.         net/ipv4/ip_forward.c:ip_forward() :需要转发的。

3.         net/ipv4/route.c:ip_error() :发生错误了,我们不能为此包找到相应的路由(routing table entry)

4.         net/ipv4/ipmr.c:ip_mr_input() :此包是个多播包,我们需要做多播路由。

2 .需要转发的包

       如果是需要转发的包,呼叫net/ipv4/ip_forwardd.c:ip_forward()

       此函数需要做的工作如下:

                                                     i.              check ttl ,如果ttl <= 1 ,丢弃该包,发送icmp time exceed 消息给发送方。

                                                   ii.              check 是否skbtailroom 足够大来容纳目的地的链路层的头部,如果必要,可以expand skb

                                                  iii.              ttl++

                                                 iv.              如果我们的包的长度 > 目的设备的MTU ,并且IP 头部的don’t fragment bit 被置位,则向发送方发送ICMP flag needed 消息。

                                                   v.              call NF_IP_FORWARD hook

                                                 vi.              如果NF_IP_FORWARD hook 返回NF_ACCEPT ,则呼叫net/ipv4/ip_forward.c:ip_forward_finish()

ip_forward_finish() 函数中检测是否需要在ip 头设置其它的一些选项,然后会呼叫include/net/ip.h:ip_send()

ip_send() 中,会检测我们是否需要对此ip 包进行分片,如果需要,则调用ip_fragment() ,否则调用net/ipv4/ip_forwardd:ip_finish_output()

ip_finish_output() 中,不干别的,只是调用另一个NETFILTER hookNF_IP_POST_ROUTING ,然后呼叫ip_finish_output2() 函数。

Ip_finish_output2() 函数中,呼叫skb 对应的硬件头的发送函数hh->hh_output(skb)

 

阅读(2072) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~