网络子系统7_l2、l3接口

//	调用路径:
//		1.NAPI设备的poll函数->netif_receive_skb
//		2.积压设备的process_backlog函数->netif_receive_skb
// 	参数:
//		skb,驱动程序已经去掉了以太帧最后的四字节crc32,skb->mac.raw指向mac头,skb->data,skb->nh指向l3帧头

//	函数主要任务:
//		1.处理netpoll衔接点
//		2.更新时间戳,dev的统计信息
//		3.处理bonding衔接点
//		4.向ETH_P_ALL类型的l3协议传递skb
//		5.处理bridge衔接点
//		6.通过数据帧标示的l3协议,传递skb到正确的l3协议
1.1 int netif_receive_skb(struct sk_buff *skb)
{
	struct packet_type *ptype, *pt_prev;
	int ret = NET_RX_DROP;
	unsigned short type;

#ifdef CONFIG_NETPOLL
	//netpoll机制
	if (skb->dev->netpoll_rx && skb->dev->poll && netpoll_rx(skb)) {
		//返回1,说明netpoll处理了这个skb,则释放skb,并返回
		kfree_skb(skb);
		return NET_RX_DROP;
	}
#endif
	//设置skb的时间戳
	if (!skb->stamp.tv_sec)
		net_timestamp(&skb->stamp);
	//处理bonding
	//在3.x的版本中,虚拟dev会注册一个handler,处理bonding
	skb_bond(skb);
	//更新本cpu接收到的数据包个数
	__get_cpu_var(netdev_rx_stat).total++;

	//将l4,l3报头对应的指针,指向skb->data
	skb->h.raw = skb->nh.raw = skb->data;
	skb->mac_len = skb->nh.raw - skb->mac.raw;

	pt_prev = NULL;

	rcu_read_lock();

	...
	//向所有ETH_P_ALL类型的l3协议,传递一份skb
	list_for_each_entry_rcu(ptype, &ptype_all, list) {
		//如果对应的l3协议没有指定dev,或者l3指定的dev就是接收到本skb的dev
		if (!ptype->dev || ptype->dev == skb->dev) {
			if (pt_prev)
				//向对应l3传递skb
				ret = deliver_skb(skb, pt_prev);
			pt_prev = ptype;
		}
	}

	//处理分流器
	handle_diverter(skb);
	//处理桥接
	if (handle_bridge(&skb, &pt_prev, &ret))
		goto out;
	//获取skb的l3协议
	type = skb->protocol;
	//hash到l3协议对应的链表,会存在协议号相同的l3协议,向所有协议号相同的处理函数传递一份skb。
	list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {
		if (ptype->type == type &&
		    (!ptype->dev || ptype->dev == skb->dev)) {
			if (pt_prev)
				//将skb传递给对应的l3协议
				ret = deliver_skb(skb, pt_prev);
			pt_prev = ptype;
		}
	}

	//处理遍历过程中最后一个l3协议
	if (pt_prev) {
		ret = pt_prev->func(skb, skb->dev, pt_prev);
	} else {
		kfree_skb(skb);
		ret = NET_RX_DROP;
	}

out:
	rcu_read_unlock();
	return ret;
}

//	调用路径:
//		1.邻居子系统邻居项output->dev_queue_xmit
//		2.l2帧头缓存output->dev_queue_xmit
//		3.其他l3协议

//	参数:
//		skb,已经填充好mac头,l3,l4协议头,skb->data指向l2帧头
//	函数主要任务:
//		1.处理skb与驱动能力不匹配的情况
//			1.1 skb->frag_list分片,驱动不能处理frag_list,
//			1.2 skb->frags分散聚集内存,驱动不能处理分散内存
//			1.3 重新分配sk_buff,拷贝所有数据到sk->data中
//		2.处理上层协议与驱动能力不匹配的情况
//			2.1 上层要求硬件计算校验和,驱动不能提供此能力
//			2.2 由软件计算校验和
//		3.关中断,禁止下半部
//		4.传输
//			4.1 驱动程序使用队列规则
//				4.1.1 获取dev->queue_lock
//				4.1.2 入队skb
//				4.1.3 通过qdisc_run启动队列进行传输
//				4.1.4 释放dev->queue_lock
//			4.2 驱动程序没有使用队列规则
//				4.2.1 获取xmit_lock锁
//				4.2.2 通过hard_start_xmit进行传输
//				4.2.3 释放xmit_lock锁

2.1 int dev_queue_xmit(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
	struct Qdisc *q;
	int rc = -ENOMEM;
	//1.如果,skb被分片,设备不能处理skb->frag_list,重新分配sk_buff,
	//将skb_shinfo(skb)->frag_list中的数据拷贝到新skb->data
	if (skb_shinfo(skb)->frag_list &&
	    !(dev->features & NETIF_F_FRAGLIST) &&
	    __skb_linearize(skb, GFP_ATOMIC))
		goto out_kfree_skb;

	//2.如果,skb使用分散聚集内存,设备不支持分散聚集IO,或者skb处于高端dma内存,
	//重新分配skb_buff,将skb_shinfo(skb)->frags中的数据拷贝到skb->data
	if (skb_shinfo(skb)->nr_frags &&
	    (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
	    __skb_linearize(skb, GFP_ATOMIC))
		goto out_kfree_skb;

	//在1,2中,只要有一个成立,就会通过__skb_linearize将skb_shinfo(skb)->frags,
	//skb_shinfo(skb)->frag_list中的数据拷贝新分配的sk_buff

	//3.l4协议要求使用硬件计算校验和,但是设备驱动没有提供校验和功能,
	//通过软件方式完成校验和
	if (skb->ip_summed == CHECKSUM_HW &&
	    (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
	     (!(dev->features & NETIF_F_IP_CSUM) ||
	      skb->protocol != htons(ETH_P_IP))))
	      	if (skb_checksum_help(skb, 0))
	      		goto out_kfree_skb;

	//1.设备驱动使用规则队列,通过队列规则完成数据传输
	//关软中断,获取dev的队列规则
	local_bh_disable();
	q = rcu_dereference(dev->qdisc);
	//队列规则具有入队功能
	if (q->enqueue) {
		//获取队列锁
		spin_lock(&dev->queue_lock);
		rc = q->enqueue(skb, q);
		//在获取设备队列锁之后,启动规则队列的数据传输
		qdisc_run(dev);

		spin_unlock(&dev->queue_lock);
		rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc;
		goto out;
	}

	//设备没有使用队列规则,直接通过驱动程序完成传输
	if (dev->flags & IFF_UP) {
		int cpu = smp_processor_id();
		if (dev->xmit_lock_owner != cpu) {
			//获取xmit_lock
			HARD_TX_LOCK(dev, cpu);
			//如果设备传输队列没有被关闭
			if (!netif_queue_stopped(dev)) {
				//向ETH_P_ALL类型的l3协议传递一份skb
				if (netdev_nit)//netdev_nit表示ETH_P_ALL类型l3协议的个数
					dev_queue_xmit_nit(skb, dev);

				rc = 0;
				//通过驱动程序完成传输
				if (!dev->hard_start_xmit(skb, dev)) {
					HARD_TX_UNLOCK(dev);
					goto out;
				}
			}
			//对xmit_lock解锁
			HARD_TX_UNLOCK(dev);
			if (net_ratelimit())
				printk(KERN_CRIT "Virtual device %s asks to "
				       "queue packet!\n", dev->name);
		} else {

			if (net_ratelimit())
				printk(KERN_CRIT "Dead loop on virtual device "
				       "%s, fix it urgently!\n", dev->name);
		}
	}

	rc = -ENETDOWN;
	local_bh_enable();

out_kfree_skb:
	kfree_skb(skb);
	return rc;
out:
	local_bh_enable();
	return rc;
}
时间: 2024-11-02 15:23:13

网络子系统7_l2、l3接口的相关文章

Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理子系统.网络子系统.虚拟文件系统在这个阶段进行分析的,但是为了让大家对内核有个整体的把握,今天还是简单的介绍一下剩余的几个子系统,我们对这几个子系统的分析,只要了解其作用和部分内容即可,不必深究,等我们写上几个驱动,到时候按照驱动再来分析这几个子系统我们就清晰多了. 在http://www.cnbl

求一款App的网络数据监控(接口的请求频率和数据量)的SDK

问题描述 求一款网络数据监控(接口的请求频率和数据量)的SDK,之前云测的一个版本有过,但是被砍掉了:newrelic,有bug:听云数据不全. 解决方案 帮你问了下,试下oneapm吧

网络子系统14_邻居子系统通用接口

//创建一个新的邻居项 //参考 深入理解linux网络技术内幕 // 1.邻居子系统为具体的邻居协议,提供通用的功能接口 // 2.系统中所有的邻居协议被链接在neigh_tables链表中 // 3.neigh_table代表一个具体的邻居协议 // 4.具体邻居协议在运行时的行为,可以通过struct neigh_parms调节, // neigh_params与设备关联,每个邻居协议neigh_table提供一个默认的neigh_params. //注册一个邻居协议到系统中 // 1.与

网络子系统22_队列规则传输接口

// 调用路径: // 1.dev_queue_xmit->qdisc_run // 2.net_tx_action->qdisc_run // 对于dev->tx_queue_len !=0 的设备,都会有一个与之关联的队列规则,按照队列规则,调用dev->hard_start_xmit 1.1 static inline void qdisc_run(struct net_device *dev) { //设备传输队列没有被关闭,即__LINK_STATE_XOFF没有被设置

网络子系统30_桥接子系统通用接口

// 添加网桥设备 // 参数: // name,需要全局唯一 // 调用路径:socket ioctl->br_add_bridge // 函数主要任务: // 1.创建一个新的网络设备 // 2.初始化网络设备的通用字段以及网桥设备的字段 // 3.向系统注册网络设备 1.1 int br_add_bridge(const char *name) { struct net_device *dev;//net_bridge->dev int ret; dev = new_bridge_dev

网络子系统85_inet协议族-l3向上

// ip数据报向本地传递 // 调用路径:ip_rcv->dst_input->...->ip_local_deliver 1.1 int ip_local_deliver(struct sk_buff *skb) { //如果ip数据报被分片,则重组 if (ip_is_fragment(ip_hdr(skb))) { if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER)) return 0; } //通过hook点,向上传递 return NF_H

网络子系统39_inet_peer缓存通用接口

// 查找inet_peer // 参数: // daddr,ip地址 // create,指示daddr不存在时,是否新建 // 注:inet_peer在内存中组织为平衡树的形式 1.1 struct inet_peer *inet_getpeer(__be32 daddr, int create) { struct inet_peer *p, *n; struct inet_peer **stack[PEER_MAXDEPTH], ***stackptr; //获取读锁 read_lock_

网络子系统11_arp子系统初始化

// 1.邻居: // 网络中的邻居指连接到同一个LAN中,且至少有一个接口有相同的ip子网配置 // 2.邻居协议的作用: // 通过l3地址获取主机的l2地址 // 3.通过l3获取l2地址办法: // 3.1 点到点连接,此时不需要l2地址 // 3.2 特殊的l3地址,通过简单的规则获取l2的映射 // 3.3 多播地址通过简单的转换规则转换为l2地址 // 3.4 ip中使用arp协议完成映射 // arp协议的初始化 // 向内核邻居子系统注册arp地址解析协议. // 调用路径:i

网络子系统15_arp邻居项初始化

// 初始化struct neighbour // 当邻居子系统新创一个neighbour时,邻居子系统调用特定协议的初始化函数,初始化邻居项. // 调用路径:neigh_create->arp_constructor // 函数主要任务: // 1.设置邻居项的地址类型,a,b,c,d // 2.使用与此邻居项关联的接口设备的neigh_param作为该邻居项调整邻居协议的参数. // 3.根据与此邻居项关联的接口设备的信息,初始化邻居项的状态,以及ops // 3.1 驱动没有提供填充l2