• 网络子系统42_ip协议处理函数_数据帧的接收


    //向协议栈注册l3处理函数
    1.1 void dev_add_pack(struct packet_type *pt)
    {
    	int hash;
    	//ptype_all ptype_base共用一把锁 ptype_lock
    	spin_lock_bh(&ptype_lock);
    	if (pt->type == htons(ETH_P_ALL)) {//ETH_P_ALL类型的l3协议,从外接收到的数据帧,和从本地发送的数据帧,都会向ptype_all链表中的l3协议,传递一份
    		netdev_nit++;
    		list_add_rcu(&pt->list, &ptype_all);
    	} else {
    		hash = ntohs(pt->type) & 15;//ptype_base有16个bucket
    		list_add_rcu(&pt->list, &ptype_base[hash]);//链接到bucket的链表中
    	}
    	spin_unlock_bh(&ptype_lock);
    }
    
    //ip协议控制块
    //每个l3协议,通过struct packet_type描述自己
    1.2 static struct packet_type ip_packet_type = {
    	.type = __constant_htons(ETH_P_IP),//.type使用网络字节序,大端模式
    	.func = ip_rcv,//ip接收函数
    };
    
    //ip协议的接收函数
    1.3 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
    {
    	struct iphdr *iph;
    	//skb->pkt_type由接收设备的驱动程序,调用eth_type_trans设定
    	//当设备处于混杂模式下,对于非本机,非广播,非多播的数据帧,设置其pkt_type=PACKET_OTHERHOST
    	//ip协议不处理PACKET_OTHERHOST类型的封包,此类型的封包应该由l3协议之前被bonding,或者网桥来处理
    	if (skb->pkt_type == PACKET_OTHERHOST)
    		goto drop;
    
    	IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);//snmp协议
    
    	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {//如果skb被共享的话,复制一份
    		IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
    		goto out;
    	}
    
    	if (!pskb_may_pull(skb, sizeof(struct iphdr)))//使skb->data到skb->tail之间足够20字节的ip头(此时不包括选项字段)
    		goto inhdr_error;
    
    	iph = skb->nh.iph;//ip头
    
    	 //数据帧可接收的条件
    	 //1.包含ip头,ipv4,校验和正确,数据包长度正确
    	if (iph->ihl < 5 || iph->version != 4)
    		goto inhdr_error; 
    
    	if (!pskb_may_pull(skb, iph->ihl*4))//ihl以4字节为单位,表示ip头的长度
    		goto inhdr_error;
    
    	iph = skb->nh.iph;//此时iph包含选项字段
    
    	if (ip_fast_csum((u8 *)iph, iph->ihl) != 0)//对ip头和选项(如果存在的话),计算校验和
    		goto inhdr_error; 
    
    	{
    		__u32 len = ntohs(iph->tot_len); //ip报文总长(包括ip报头,选项,ip有效载荷)
    		if (skb->len < len || len < (iph->ihl<<2))//skb中l3数据总长度小于接收到的长度,或者接收到的长度不足ip报头和选项的长度
    			goto inhdr_error;
    
    		if (skb->len > len) {//skb中l3数据总长度大于ip报头中指示的总长度
    			__pskb_trim(skb, len);//切割skb从data开始的数据长度到len字节
    			if (skb->ip_summed == CHECKSUM_HW)//如果校验和已经由硬件求得,但是由于对数据进行了切割操作
    				skb->ip_summed = CHECKSUM_NONE;//则指示需要网络层通过软件再进行校验和计算
    		}
    	}
    
    	return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
    		       ip_rcv_finish);//经过netfilter钩子函数的处理,最终由ip_rcv_finish完成处理
    
    inhdr_error:
    	IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
    drop:
            kfree_skb(skb);
    out:
            return NET_RX_DROP;
    }
    
    1.4 static inline int ip_rcv_finish(struct sk_buff *skb)
    {
    	struct net_device *dev = skb->dev;
    	struct iphdr *iph = skb->nh.iph;
    
    	if (skb->dst == NULL) {//目标路由未设置
    		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))//输入路径上的路由,设置skb->dst
    			goto drop; 
    	}
    	...
    
    	if (iph->ihl > 5) {//需要处理ip选项
    		struct ip_options *opt;
    
    		if (skb_cow(skb, skb_headroom(skb))) {//skb->data 到 skb->head之间,保证16个字节的空间
    			IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
    			goto drop;
    		}
    		iph = skb->nh.iph;
    		//在报文接收路径的多处,由不同函数对各个选项进行处理
    		if (ip_options_compile(NULL, skb))//此处分析ip选项,初始化skb->cb中ip_options结构,为后边处理ip选项做准备。
    			goto inhdr_error;
    
    		opt = &(IPCB(skb)->opt);
    		if (opt->srr) {//处理源路由选项
    			struct in_device *in_dev = in_dev_get(dev);
    			if (in_dev) {
    				if (!IN_DEV_SOURCE_ROUTE(in_dev)) {//入口设备的inet配置不支持源路由选项
    					if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
    						printk(KERN_INFO "source route option %u.%u.%u.%u -> %u.%u.%u.%u
    ",
    						       NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
    					in_dev_put(in_dev);
    					goto drop;//直接丢弃封包
    				}
    				in_dev_put(in_dev);
    			}
    			if (ip_options_rcv_srr(skb))//处理员路由选项
    				goto drop;
    		}
    	}
    
    	return dst_input(skb);//skb->dst->input处理,input函数指针由ip_route_input,根据目标地址,进行设置。
    
    inhdr_error:
    	IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
    drop:
            kfree_skb(skb);
            return NET_RX_DROP;
    }
    
    //ip选项处理路径 参考 深入理解linux网络技术内幕

                                                       

  • 相关阅读:
    SQL Server IF Exists 判断数据库对象是否存在的用法
    C# RDLC报表不出现预览窗体直接输出到打印机
    C# 未安装Office环境下使用NPOI导出Excel文件
    C# 键盘中的按键对应KeyValue
    微信小程序下可以使用的MD5以及AES加密(通用)
    SQL Server 根据树状结构表生成以/号分割的路由字符串
    C# Winform下一个热插拔的MIS/MRP/ERP框架16(窗体基类场景2)
    WEB H5 JS QRCode二维码快速自动生成
    C# Winform 小技巧(Datagridview某一列按状态显示不同图片)
    获取请求地址的IP地址
  • 原文地址:https://www.cnblogs.com/riskyer/p/3357965.html
Copyright © 2020-2023  润新知