分类: 嵌入式
2016-01-12 14:25:02
一、前言
前几天做协议划分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。
数据结构变动图