转:http://blog.sina.com.cn/s/blog_62bbc49c0100fs0n.html

一、前言

前几天做协议划分vlan的时候看了一些linux内核,了解不深,整理了下vlan相关部分的学习笔记,如果有理解上的错误,欢迎指正,以下代码来自于2.6.14-triny(PPC架构)。

VLAN(Virtual Local Area
Network)的中文名为"虚拟局域网"。VLAN是一种将局域网设备从逻辑上划分成一个个网段,从而实现虚拟工作组的新兴数据交换技术。IEEE于1999年颁布了用以标准化VLAN实现方案的802.1Q协议标准草案。

二、概述
   
看代码的时候发现linux网络内核基本就是按照分层模型来设计的,于是把内核的网络框架按照分层模型来理解:

物理层L1 
    对应网卡驱动模块(driver/net),主要负责从物理介质接受和发送数据。当然,在内核外还有switch,因此switch上的配置是凌驾于一切的,如果switch上没有设置vlan
tag属性,一切对vlan的修改都无从谈起。
链路层L2 
    对应网络核心接口模块(net/core/),与具体设备无关。core核心负责了大量接口之间的连接和调度,统一不同网卡的驱动程序与网络协议层的接口,而实际上很多L2层以太网头的打包剥离过程是在L1驱动层做的。

网络层L3,传输层L4 
    对应协议栈模块(net),负责对报文的层层解析,逐部剥离IP层,TCP/UDP层等等。其中ipv4的主要代码是net/ipv4/af_inet.c。

会话层L5 
    对应socket模块,与协议无关,是为用户提供网络服务的编程接口,提供了系列系统调用,主要文件是net/socket.c。

vlan模块工作在L2核心接口和L3协议栈之间,遵循802.1Q协议,是通过构造一个虚拟的网口来触发vlan功能的,主要文件在net/8021q下。

三、内核vlan实现方式
1.   
通过Vconfig配置一个虚拟的vlan设备,并进行基本的配置。
    vconfig add
eth0 y ;y为需要设置的vlan tag id.
    vconfig
set_flag eth0.y 1 1 ;设置一些vlan属性.

2.   
通过ifconfig接口为该虚拟设备配置ip,实际上就是修改路由表,让报文从该设备发送或者从该设备接收。
    Ifconfig
eth0.y 192.168.xx.xxx

3.   
OK了,接下来通过该eth0.y发送的报文均被打上了vlan tag y,接收到的报文均会被去除vlan tag。

下面分别通过分析8032环境下发送和接受的案例模型,理清各层间的架构关系。

四、发送流程

对于L5来说,一般的网络通讯,首先是建立一个socket结构,sockfd =
socket(AF_INET,SOCK_RAW,protocol->p_proto)执行系统调用sys_socketcall(/net
/socket.c),接着sendto函数调用系统调用sys_sendto(/net/socket.c),处理完用户空间和内核空间的转换后,进入
L3。

L3层一方面包装好sk_buffer,一方面调用ip_route_output_flow路由表查询函数,找到net_device网卡设备,内核中的vlan模块就是注册了这样一个模拟的网络设备(eth0.y)。

对于L2来说,
L3协议栈通过dev_queue_xmit(dev.c)调用dev->hard_start_xmit(),关联到了具体的网络设备处理函数,从而调用了vlan_dev_hard_start_xmit(vlan_dev.c),在这里生成vlan
tag头,并压入sk_buffer中,然后找到真实的网络设备(real_dev),再次调用dev_queue_xmit,进入真实的网卡驱动L1层。

对于L1来说,hard_start_xmit就是fcc_enet.c的fcc_enet_start_xmit函数,将L2层送来的skb压入队列
中,修改寄存器,将报文发送出去。注意到这时候的skb已经是一个很完整的结构了,可以很容易对skb做各种判断和处理,此次的按协议划分vlan最后就
是在这里完成。虽然到现在也觉得在这里修改实在是太不优雅,太不易扩展了,但不得不承认,因为skb的完整,在这里修改的工作量是最小的,而且不需要对
linux内核网络框架有什么深入的了解就可以着手修改。

发送流程图

五、接收流程

对于L1来说,接收工作比发送复杂一些,在初始化时注册一个中断处理函数,request_irq(fip->fc_interrupt,
fcc_enet_interrupt, 0, "fenet", dev) <
0) 
当收到一个新的报文,会触发一个软中端SIU_INT_FCC1,调用fcc_enet_interrupt函数,进而调用fcc_enet_rx进入接收处理。Rx函数先分配一个sk_buffer,将从寄存器取来的报文填入该buffer中。

接下来就是将buffer中的以太网头部剥离,目前8032的按协议划分vlan的接收部分就是在此完成,剥离以太网头部时发现proto为8021Q,则暴力修改以太网帧,剥离vlan层,重置以太网层的proto。

数据结构变动图

上面的流程图是内核中常用的去除某个报文头的方法,Sk_buff的总大小是不变的,只是针对指针位置将数据作了必要的位移,甚至连14bit的以太网头部的最后2bit协议类型都因为已经赋值了skb->proto而不做memmov。

之后就可以通过netif_rx进入协议栈了。netif_rx调用napi_schedule调度,挂接当前napi_struct到处理CPU的
napi_struct中,发送NET_RX_SOFTIRQ软中断,触发net_rx_action(/net/core/dev.c)。
(NET_RX_SOFTIRQ的注册在net_dev_init中)[code]open_softirq(NET_RX_SOFTIRQ,
net_rx_action, NULL);[/code]
net_rx_action这里会历遍所有的softnet_data结构,执行其poll操作process_backlog(/net/core
/dev.c),调用netif_receive_skb(skb)来处理收到的skb数据,最后唤醒recvfrom调用。

而内核中的vlan模块就是工作在协议栈里,通过注册dev_add_pack注册了8021Q的协议类
型,netif_receive_skb遍历所有的协议类型时发现了它,于是进入了vlan的接收函数
vlan_skb_recv(vlan_dev.c),这个函数里剥离vlan层,重置以太网层的proto,重新调用netif_rx进入真正的协议栈
处理流程,例如,L3是IP协议,则进入ip_rcv(/net/ipv4/ip_input.c)

对于L5来说,recvfrom系统调用sys_recvfrom在/net/socket.c中,通过prepare_to_wait_exclusive等待skb的到来,从而唤醒它。

接收流程图

协议栈模块其实是块大头,研究不深入,就简要概括了。

----------------------------------纠结的分割线--------------------------------

六、进一步(路由模块):

还有一个很重要的模块就是内核路由子系统,它应该是工作在L3层。发送报文时会在L3层打好IP头前,路由到具体的网络设备,然后才进入L2填充以太网头。

路由的工作是通过函数ip_route_input()(/net/ipv4/route.c)实现的,对于进来的包可能的路由有这些:

 
属于本地的数据(即是需要传递给TCP,UDP,IGMP这些上层协议的) ;
   
需要要转发的数据包(网关或者NAT服务器之类的);
    不可能路由的数据包(地址信息有误);

当路由发现是本地数据时,rth->u.dst.input =
ip_local_deliver,返回后就可以在skb->dst->input(skb)中进入ip_local_deliver,找到对应的interface设备。如我们的eht0.y
vlan虚拟设备,进入vlan处理流程。

vlan linux内核数据流程的更多相关文章

  1. Linux内核数据包的发送传输

    本文主要讲解了Linux内核数据包的传输流程,使用的内核的版本是2.6.32.27 为了方便理解,本文采用整体流程图加伪代码的方式从内核高层面上梳理了二层数据包发送传输的流程,希望可以对大家有所帮助. ...

  2. 【内核】linux内核启动流程详细分析

    Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件,包括内核入口ENTRY(stext)到start_kernel间的初始化代码, 主要作用 ...

  3. 【内核】linux内核启动流程详细分析【转】

    转自:http://www.cnblogs.com/lcw/p/3337937.html Linux内核启动流程 arch/arm/kernel/head-armv.S 该文件是内核最先执行的一个文件 ...

  4. linux 内核启动流程

    Linux内核启动流程详细分析: http://www.linuxidc.com/Linux/2014-10/108034.htm ARM Linux内核启动过程: http://blog.csdn. ...

  5. linux内核数据包转发流程(三)网卡帧接收分析

    [版权声明:转载请保留出处:blog.csdn.net/gentleliu.邮箱:shallnew*163.com] 每一个cpu都有队列来处理接收到的帧,都有其数据结构来处理入口和出口流量,因此,不 ...

  6. Linux内核启动流程(简介)

    1. vmlinux.lds 首先分析 Linux 内核的连接脚本文件 arch/arm/kernel/vmlinux.lds,通过链接脚本可以找到 Linux 内核的第一行程序是从哪里执行的: 第 ...

  7. linux 内核启动流程分析,移植

    分析 linux-2.6.22.6 内核启动流程 移植 linux-3.4.2 到 JZ2440 开发板 Linux内核源码百度云链接: https://pan.baidu.com/s/1m1ymGl ...

  8. Linux内核启动流程与模块机制

    本文旨在简单的介绍一下Linux的启动流程与模块机制: Linux启动的C入口位于/Linux.2.6.22.6/init/main.c::start_kernel() 下图简要的描述了一下内核初始化 ...

  9. Linux内核启动流程分析(二)【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3380544.html S3C2410 Linux 2.6.35.7启动分析(第二阶段) 接着上面的分析,第 ...

随机推荐

  1. Threadlocal源码分析以及其中WeakReference作用分析

    今天在看Spring 3.x企业应用开发实战,第九章 Spring的事务管理,9.2.2节ThreadLocal的接口方法时,书上有提到Threadlocal的简单实现,我就去看了下JDK1.8的Th ...

  2. bash-1 初始化CentOS系统的初始化脚本

    初始化CentOS系统的初始化脚本 #!/bin/bash # #******************************************************************* ...

  3. golang中字符串内置函数整理

    字符串内置函数 1. 判断字符串的长度 str := "korea国" fmt.Println("str len=", len(str)) 2. 字符串遍历,同 ...

  4. 在vue中使用Normalize初始化样式

    参考链接:https://www.jianshu.com/p/34533b45aac1

  5. day38 各种队列、Event事件、协程、猴子补丁

    1.各种队列 我们已经学习了队列这种存取值的方法,我们以前使用的队列是可以进行进程间通信的(IPC),但是今天学习的这两种队列是不能进行进程间通信的,只能进行线程间的通信 这两种队列分别是先进后出式队 ...

  6. C语言Ⅰ博客作业09

    这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-3/homework/10029 我在这个课程的 ...

  7. 选择排序的Python代码实现

    对于a[0]~a[n]的数组, 默认a[i]最小,和后面的a[i+1]~a[n]进行比较,把最小的和a[i]交换位置,保证本次循环结束后a[i]是上一次未排序的数据中最小的 写法1 a=[12,2,2 ...

  8. jQuery+php+ajax实现无刷新上传文件功能

    jQuery+php+ajax实现无刷新上传文件功能,还带有上传进度条动画效果,支持图片.视频等大文件上传. js代码 <script type='text/javascript' src='j ...

  9. MyBatis学习存档(1)——入门

    一.简介 MyBatis的前身是iBatis,本是Apache的一个开源的项目 MyBatis是一个数据持久层(ORM)框架,把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现 MyB ...

  10. mysql之存储过程基础

    存储过程 procedure 可以理解为一个处理增删改,没有返回值得函数 创建存储过程的基本语法 create procedure 过程名 ([参数列表]) begin --过程体 end 存储过程主 ...