分类: LINUX
2012-05-23 16:54:29
ip_rcv:
1. 它首先判断包的类型,如果是去往其他主机的包,则直接丢弃。
2. 若skb是共享的,则创建一个新的sk_buff结构,并拷贝skb。数据缓冲区依然保持共享。
3. 确保最小的IP头部已经在线性数据缓冲区中,即在skb->data所指向的内存中。否则需要从skb_shinfo(skb)->frags[]中拷贝。可能有些驱动把数据放入了非线性区域。这种情形是相当少的。
4. 对IP头部的各个字段进行严格检查,看其是否合法,若非法,则丢弃该包。
5. 同3,只不过这次要确保实际IP头部在线性数据缓冲区中。
6. 验证IP头部校验和及skb和头部的长度有效性。
7. 由于L2层可能会对包进行padding,将skb的数据缓冲区调整为IP头部指定的确切长度。
8. 调用ip_rcv_finish。
ip_rcv->ip_rcv_finish:
1. 若该包的路由信息为空,则调用ip_route_input获取路由信息,路由信息在skb->dst字段中。
2. 若IP头部大于20字节,则调用ip_rcv_options处理选项。
3. 实质调用skb->dst.input()。若递交到本地则是ip_local_deliver,若转发则是ip_forward
ip_rcv->ip_rcv_finish->ip_local_deliver:
1. 如果该skb是一个分段IP包,则调用ip_defrag进行IP分段包重组。
2. 调用ip_local_deliver_finish
ip_rcv->ip_rcv_finish->ip_local_deliver_finish:
最简单的情形下,该函数根据IP头部的协议字段找到处理该协议的上层协议处理入口并调用。数据包离开IP层。
IP分片重组
ip_rcv->ip_rcv_finish->ip_local_deliver->ip_defrag:
1. 检查IP分片所消耗的内存是否大于系统设置的最高阀值,若是,则调用ip_evictor函数开始清理分片,从最旧的开始清理,直至达到系统设置的最低阀值。
2. 调用ip_find找到该分片的分片等待队列。若其是第一个达到的分片,则会分配一个分片等待队列。
3. 调用ip_frag_queue将该分片加入到其所属分片等待队列,对所有分片进行重组,消除可能存在的重叠。ip_frag_reasm负责对所有分片到达时对分片进行重组。
ip_rcv->ip_rcv_finish->ip_local_deliver->ip_defrag->ip_find:
设置好inet_frag_find所需的参数并调用它,进行实质性的工作由这个函数完成。
ip_rcv->ip_rcv_finish->ip_local_deliver->ip_defrag->ip_find->inet_frag_find:
扫描哈希槽队列中是否有容纳该分片的分片等待队列,若找到则返回该队列,否则调用inet_frag_create创建一个分片等待队列。
ip_rcv->ip_rcv_finish->ip_local_deliver->ip_defrag->ip_find->inet_frag_find->inet_ftag_create:
1. 调用inet_frag_alloc创建一个新的分片等待队列,并进行初始化。
2. 调用inet_frag_intern将该队列加入到哈希表中并加入的lru_list链表中。