一 前面一节我们讲了skb通过事件通知到了软中断处理,最终调用deliver_skb分发出去skb,
注意deliver_skb调用这里是一个循环,list_for_each_entry_rcu ,netfilter模块就是这里调用的,因此可以知道,抓包工具等对socket读写是没有影响的,这里浅拷贝了一份skb到netfilter模块当实参传递的。而读写socket是更上层的调用,不属于同一个模块。
二 分析上层协议栈处理流程
deliver_skb()
{
Ip_rcv()->Ip_rcv_finish()
{
Dst_input();
}
}
//这里到dst_input分为两种情况,如果是本地数据包,即dst等于自己的,抛到上层协议栈处理,调用ip_local_deliver()。如果不是本地的数据包,路由出去,调用ip_forward()
三 本地处理ip_local_deliver()
{
ip_is_fragment()判断是否是IP分组
{
ip_defrag()
{//首先调用ip_find查找是否已经在分组队列里面存在相应的queue,如果没有
//就新建一个queue等待手机之后的报文,如果已经存在就返回queue的入口,ip_find()主要是调用iphashfn(),使用ip头部里面的identifier,
//source addr,dest addr,protocol四个元素去进行hash计算吗
//而iphashfn()调用的是jash_3words,该函数是搞性能查找算法
ip_find()
}//ip 碎片重组
}
//最终通过NF_HOOK调用这个函数处理数据包
ip_local_deliver_finish()
{
ipprot = rcu_dereference(inet_protos[protocol]);
//获取传输层协议
ret = ipprot->handler(skb);//传输层协议处理skb,
这里我们仅仅分析tcp的数据包处理
}
}
tcp_v4_rcv()//这个函数包含了tcp协议的状态机变化,以及数据包处理流程
{
__TCP_INC_STATS(net, TCP_MIB_INSEGS);//调整统计值
skb_checksum_init();//checksum 检查
解析tcp协议各个字段
tcp_v4_do_rcv();//最终调用这个函数处理,当然还有一些其它状态的处理,可以自行阅读源码,
}
tcp_v4_do_rcv()
{
if (sk->sk_state == TCP_ESTABLISHED)
{
tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len);//ESTABLISHED状态处理
}
if (sk->sk_state == TCP_LISTEN)
{
tcp_rcv_state_process();
}
}
void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
const struct tcphdr *th, unsigned int len)
{
· 快速路径 使用快速路径只进行最少的处理,如处理数据段、发生ACK、存储时间戳等。
· 慢速路径 使用慢速路径可以处理乱序数据段、PAWS、socket内存管理和紧急数据等。
static inlinevoid__tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) { tp->pred_flags =htonl((tp->tcp_header_len <<26) |ntohl(TCP_FLAG_ACK) |snd_wnd); }
}
流程示意图(从http://blog.csdn.net/Al_xin/article/details/52304732借的图)
四 下一节讲ip数据包路由ip_forward和tcp_rcv_established()如何把skb抛到应用层,并激活socket可读的