linux网络协议栈分析笔记5-IP层的处理1

前几章稍分析了网桥,接着我们继续1中的收包流程,说到
对该数据包转达到其他L3协议的处理函数:
type = skb->protocol;
list_for_each_entry_rcu(ptype,
               & ptype_base[ntohs( type) & PTYPE_HASH_MASK], list) {
          if (ptype->type == type &&
              (ptype->dev == null_or_orig || ptype->dev == skb->dev ||
               ptype->dev == orig_dev)) {
               if (pt_prev)
                    ret = deliver_skb(skb, pt_prev, orig_dev);
               pt_prev = ptype;
          }
     }
->static inline int deliver_skb(struct sk_buff *skb,
                     struct packet_type *pt_prev,
                     struct net_device *orig_dev)
     {
          atomic_inc(&skb->users);
           return pt_prev-> func (skb, skb->dev, pt_prev, orig_dev);
     }
ptype_base :         协议模块通过dev_add_pack()加入     arp  pppoe  ip等
我们看ptype_base的建立
void dev_add_pack(struct packet_type *pt)
{
     int hash;

     spin_lock_bh(&ptype_lock);
     if (pt->type == htons( ETH_P_ALL))                    如果是ETH_P_ALL类型,则加入ptype_all 用于sniffer等
          list_add_rcu(&pt->list, & ptype_all);
     else {
          hash = ntohs(pt->type) & PTYPE_HASH_MASK;    否则使用hash方式插入ptype_base中
          list_add_rcu(&pt->list, & ptype_base[hash]);
     }
     spin_unlock_bh(&ptype_lock);
}

众多协议通过此接口注册协议处理功能 
dev_add_pack(&arp_packet_type);          arp
dev_add_pack(& ip_packet_type);            ip
dev_add_pack(&ipv6_packet_type);         ipv6
dev_add_pack(&vlan_packet_type);         vlan-802.1q
dev_add_pack(&pppoes_ptype);             pppoes
dev_add_pack(&pppoed_ptype);             pppoed

当然我们主要看dev_add_pack(& ip_packet_type);            ip
->inet_init()  /net/ipv4/af_inet.c
     ->dev_add_pack(& ip_packet_type );
          static struct packet_type ip_packet_type __read_mostly = {
          .type = cpu_to_be16(ETH_P_IP),                  #define ETH_P_IP     0x0800          /* Internet Protocol packet     */
            .func = ip_rcv,
          .gso_send_check = inet_gso_send_check,
          .gso_segment = inet_gso_segment,
          .gro_receive = inet_gro_receive,
          .gro_complete = inet_gro_complete,
          }; ip协议入口
->ip_rcv()
     ->if (skb->pkt_type == PACKET_OTHERHOST)  
          goto drop;           pkt_type表示报文类型 。PACKET_OTHERHOST表示非去往本机但是在特定模式下被接受的报文
     ->pskb_may_pull(skb, sizeof(struct iphdr))    pskb_may_pull在调用 skb_pull() 去掉外层协议头之前,通常先调用此函数判断一下是否有足                           
                                                                           够的数据用于“pull”。
     ->iph = ip_hdr(skb);  取得ip头
     ->if (iph->ihl < 5 || iph->version != 4)    普通IP数据包首部长度(不包含任何选项)字段的值是5
               goto inhdr_error;     iph->ihl<5说明iph->ihl指的是IP包的首部长度,首部一行是32bit也就是4byte(字节)
     ->if (!pskb_may_pull(skb, iph->ihl*4))      iph->ihl*4是20,是首部最长的长度,此语句是说如果头部长度不能pull,则error
               goto inhdr_error;
     ->if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))      用汇编来校验和
          goto inhdr_error; 
     ->NF_HOOK(PF_INET, NF_INET_PRE_ROUTING, skb, dev, NULL,
                 ip_rcv_finish);            交给netfilter过滤,过滤通过则交给ip_rcv_finish处理
->ip_rcv_finish()
     ->ip_route_input()  查找路由信息
     ->if (iph->ihl > 5 && ip_rcv_options(skb)) 如果IP头部大于20字节,则表示IP头部包含IP选项,需要进行选项处理.
          goto drop;
     ->dst_input(skb);      dst_input实际上会调用skb->dst->input(skb).input函数会根据路由信息设置为合适的函数指针,如果是递交到本地的               
                                        则为ip_local_deliver,若是转发则为ip_forward
两条路径:
1)   ip_local_deliver
2) ip_forward
先看 ip_local_deliver
->ip_local_deliver()
          ->if   (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {   判断是否为分片报文
               if ( ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))          进行重组
                    return 0;
                    }
          -> ip_local_deliver_finish();
->ip_local_deliver_finish()
     ->__skb_pull(skb, ip_hdrlen(skb));   剥离IP头
     ->skb_reset_transport_header(skb);    置传输层的头
     ->protocol = ip_hdr(skb)->protocol 得到协议号
     ->raw_local_deliver()  ->raw_v4_input() 进入raw socket处理流程
     ->hash = protocol & (MAX_INET_PROTOS - 1);   
          ipprot = rcu_dereference( inet_protos[hash]

struct net_protocol * inet_protos[MAX_INET_PROTOS];

int   inet_add_protocol(const struct   net_protocol  *prot, unsigned char protocol)
{
     int hash, ret;

     hash = protocol & (MAX_INET_PROTOS - 1);

     spin_lock_bh(&inet_proto_lock);
     if (inet_protos[hash]) {
          ret = -1;
     } else {
            inet_protos[hash] = prot;
          ret = 0;
     }
     spin_unlock_bh(&inet_proto_lock);

     return ret;
}
 inet_protos[]是INET SOCKET层全部协议的接收处理函数集的一个数组,每个协议以自己的协议号(比如icmp协议就以IPPROTO_ICMP,也就是数字1作为下标,把自己添加到该数组,数组中存储了net_protocol结构,例如针对TCP协议,相关的结构定义如下:

inet_add_protocol(&icmp_protocol, IPPROTO_ICMP)
inet_add_protocol(&udp_protocol, IPPROTO_UDP)
inet_add_protocol(& tcp_protocol, IPPROTO_TCP)
inet_add_protocol(&igmp_protocol, IPPROTO_IGMP)

static const struct net_protocol   tcp_protocol  = {
     .handler =       tcp_v4_rcv,
     .err_handler =     tcp_v4_err,
     .gso_send_check = tcp_v4_gso_send_check,
     .gso_segment =     tcp_tso_segment,
     .gro_receive =     tcp4_gro_receive,
     .gro_complete =     tcp4_gro_complete,
     .no_policy =     1,
     .netns_ok =     1,
};


     ->(!ipprot->no_policy) ipprot->no_policy如果为1,表明当前处理的协议设置了IP_SEC处理规则
     ->ipprot-> handler(skb);   .handler =      tcp_v4_rcv,   若是TCP则调用该函数  数据包进入TCP协议栈继续处理

 

你可能感兴趣的:(linux网络协议栈分析笔记5-IP层的处理1)