Click模块化路由器

【概述】linux

Click是一种基于软件控制的模块化路由器。其架构能够大体视为一系列数据包处理模块(称为elements)组成的。一个Click路由器能够当作一张由elements做为顶点,数据包传递路径做为边的图。这种模块化设计使得内部功能结构清晰且易于拓展。程序员

 

1.介绍算法

如今的趋势下,路由器的功能已经再也不是单纯用于包转发,每每还同时具备地址转换、包过滤等功能,并每每扮演着防火墙一类的角色。可是,如今的路由器设计的都很封闭,管理员仅仅能控制功能的开关,却很难实现各个功能之间更为复杂的交互,而第三方开发者也很难对路由器功能进行拓展。网络

所以就有了Click模块化路由器,它能够提供细粒度的网络控制。Click由一系列的称为element的功能模块组成。这些elements都具备很窄的功能interface,提供最为atomic的功能实现,可是能够经过多个elements互相之间的拓展来完成更为复杂的功能。这些elements拼成一个有向图(directed graph),就造成了一个模块化的路由器。要拓展一个路由器的功能,程序员能够本身写新的功能模块,也能够将已有的功能模块从新组装,完成不一样的功能。 (文中提到这个idea和Unix很类似?我不太懂Unix,有空能够了解一下。)架构

 

2.架构app

正如前面所说,Click由一系列的elements构成,而全部的Click路由器的原子化的功能都包含在elements中。具体来讲,在一个running router 中,一个element就是一个C++对象,一条connection就是一个指向该对象的指针,而一个传递动做就是一次内部函数的调用。框架

一个element有以下重要的属性:dom

1) element类 (element class)ide

正如前面所述,一个element就是一个C++的对象,所以也就有一个class与之对应,element属于该element class。模块化

2) 端口(port)

一个element能够由多个输入输出的端口。全部的connection都是始自于一个element的输出端口,终于另外一个element的输入端口。

3)配置语句 (configuration string)

配置语句是一个可选项,用于在路由器初始化时配置其中的状态。

4)方法接口(method interface)

一个element能够提供一个或多个接口,如最基本的router能够提供packet transfer的接口,最基本的包转发功能,而支持队列的element能够提供回报queue length的接口。

2.1 push和pull链接

Click路由器中的链接分为push和pull两种类型。push链接是常规的包转发链接,由源element发送一个packet出来到目的element。而pull链接则是相反,由目的element向源element发出一个request索取一个包,再由源端口回复给它。一个链接是push仍是pull和链接两端的端口有关。push端口到push端口的就是push链接,pull端口到pull端口的就是pull链接。而push到pull端口的链接非法。

路由器中还有一种端口称为agnostic,也就是未指定状态的端口,当它和push端口创建链接时就是push端口,当它和pull端口创建链接时就是pull端口,所以能够看做是一个中性的状态。可是须要注意的是,当一个router要进行propagation以前,全部沿途的端口都必须赋予相应且合法的状态,在此以前路由器没法进行转发功能(propagation constraint)。

2.2 数据包存储

element的端口上并不默认包含队列(queue),在Click中,队列自己就是一个element,也就是一个对象(或说是一个类)。这也就意味着网络管理员能够显式的定义数据是如何存储的。一个Queue须要一个push的input端口和一个pull的output端口。

2.3 CPU调度

Click控制着一个任务队列(task queue),这个队列内的内容单元就是一个个element。经过调度这个队列,路由器能够安排在某个时间内哪一个element的功能能够调用CPU的资源。换言之,element既是包处理的最小单元,也是CPU占用控制的任务队列中的最小单元。可是大多数element并不须要放在任务队列中,而是经过push和pull方法轮到它时自动隐性调用的。

Click目前是以单线程运行的,也就是说从一个包转发开始,CPU跟踪这个包走过一个个element,同一时间内只会有一个element在运行处理这个包,一直到这个包被drop或被store。

2.4 Flow-Based Router Context

一个element想找到另外一个element有两种方法,一种是经过名字来调用,另外一种是经过Flow-based router context来调用(不知道怎么翻译了,简称FBRC吧)。FBRC能够用来声明一个从某一个element出来的packet通过若干次transfer之后在哪里停下来,也能够用来声明到达某一个element的packet是从哪里发出来的,说简单点就是“从哪来”和“到哪去”。更具体点说,一个element可能会向系统发问“若是我这个包从端口2发出去,它可能会到哪里去?”这类问题的答案都是FBRC通过计算之后加以回复的。FBRC使用的是一种配置拓扑图的简单数据流算法,通常来讲elements会在初始化的时候询问一次FBRC,并在路由器运行期间使用这个结果做为快速reference,而element须要具备接受多个result并在结果过多或过少时报错的能力。

2.5 Click语言

Click语言中主要包含两个重要部分,声明(declaration)和链接(connection)。声明是用来建立element的,链接则用来指明element之间是怎么互相关联的。Click语言中还包含一种抽象机制称为“复合元素”(compound element)。它能够包含多个基本element,如一个queue元素加一个shaper元素能够合并为一个Shapedqueue元素,而其余配置则能够将其视为一个总体元素加以调用。

2.6 安装配置

目前Click能够运行在两种驱动平台上,一种是Linux内核驱动(Linux in-kernal driver),一种是用户层驱动(user-level driver)。用户层驱动通常是用来作调试使用的,而内核驱动则每每用来用做生产。咱们暂时就关注于内核驱动。

要安装一个Click的配置,用户首先须要将一个写好的Click文件传到核心驱动中,由核心驱动经过分析该文件来生成相应的elements并将路由器加入网络中。一次新的配置的安装会移除旧的配置及其状态,如queue中的包等。可是,Click包含两种手段可使得原配置不被丢失:

1) Handlers: Handlers如同一个element面向用户的界面。一个element中能够包含多个handler,若是这个element叫c,那么一个名为count的handler就是一个放在proc/click/c 路径下的名为count的文件。

2)Hot swapping(热插拔):有的时候对于configuration的更改多是增添或删除element,也就意味着没法使用element内部handler进行配置的保留。此时可使用热插拔的方法,即写一个新的configuration file,只有当这个配置运行正常时才使用这个配置,若是这个配置出问题则会从新降级到本来的配置中。经过利用这种热插拔机制,Click还能够实如今路由器运行时动态增删elements。

2.7 Element的具体实现

简洁的说,一个element是一个element class的对象,一个element class是一个名为Element类的C++子类,Element中包含了20多种虚函数,可是因为大多数虚函数中已经有了默认的实现,所以在继承时其实只须要重载其中的五六种方法,而若是要实现最基本的路由功能则只须要三个方法就够了。

2.8 Element的设计体系及限制

通常来讲,用户都倾向于细粒度的element和更为简单的标准。可是有的状况下粗粒度的控制会更好一些,特别是在Click中不少对于流的处理,每每不会对于一个流内部的不一样包进行细粒度的控制,或者是一些复杂的域间协议也不会对内部的具体内容单元进行单独的控制。

传统的路由器中会包含一些并不实际参与具体路由动做的功能(而是起到一些路由前的决策和准备的做用),例如路由表,网络参数统计等,这些功能在Click中都包含在数据包的转发路径中的某些element中,例如路由表会包含在选择路径的element中,而网络统计也会包含在相应的收集模块中。这些模块也会向外界提供相应的方法来调用这些内部的功能。

Click也具备一些限制和待完善的地方:

1)Click没法让CPU针对每个特定的流来调度;

2)Click提供的FBRC机制缺少针对性(not specific);

3)Click语言不提供配置变量,例如没法将一个Ethernet address赋值给一个变量a并调用它,而必须屡次重复显式写出来这个地址值。

 

3. 一个IP 路由器

该部分展现了一种真实的Click路由器的配置。IP转发任务因为只包含本地信息,所以能够很天然的应用于Click的架构当中。举一个DecIPTTL的例子。DecIPTTL被用于检测一个进来的IP包的TTL(time-to-live)字段,若是一个包的TTL依然有效,则DecIPTTL会将该字段减1,修改checksum并从第一个端口转发出去;若是一个包的TTL已通过期,则DecIPTTL将会把这个包从端口2发送出去(每每发送至一个ICMPerror的element中),之因此这种操做都是基于本地信息,是由于诸如TTL字段的判别和decision只是包含在packet当中,而不受该包的转发路径上其余element的影响。

另外一类状况就是一个element对于包的操做使受到其余element的影响的,也就再也不是基于本地信息了。在Click中针对这类操做可使用一种称为注释(annotations)的方法,注释每每由某个element添加并跟随在包的header当中,可是并不算packet data的一部分。在本部分所举的IP router的例子中也会使用这种注释的方法,具体有以下几种:

1)目标IP地址: 虽然一个IP包的header中包含了目标地址,可是因为在路由过程当中的中间路由器可能并不须要知道IP的目标地址(例如仅仅须要知道下一跳网关地址),所以element之间可能就会频繁修改这个目标地址。可是这种转发操做不该该修改数据包自己字段,所以可使用注释的方法,element之间仅仅查看和修改对应的目标地址注释从而肯定转发路径。和目标IP地址注释一块儿使用的有几个方法:GetIPAddress用于从header中复制目标地址到annotation中;LookupIPRoute用于替换当前annotation到下一个IP网关地址;ARPQuerier则用于将当前annotation映射到下一个以太网地址。

2)着色(paint ):着色element能够讲一个包标记为一个整型的“颜色”,CheckPaint能够将一个包从它的第一转发口发送出去,并在次转发口发送出该包着色之后的备份,IP路由器经过这种机制能够知道一个数据包是不是从它的接受端口发送出去,从而能够探测这种转发并触发ICMP重定向(ICMP Redirection)。

*附:什么是ICMP重定向【来源:百度百科】

ICMP重定向报文是ICMP控制报文中的一种。在特定的状况下,当路由器检测到一台机器使用非优化路由的时候,它会向该主机发送一个ICMP重定向报文,请求主机改变路由。路由器也会把初始数据包向它的目的地转发。

发生ICMP重定向一般有两种状况:

1)当路由器从某个接口收到数据还须要从相同接口转发该数据时;

2)当路由器从某个接口到发往远程网络的数据时发现源ip地址与下一跳属于同一网段时。 

3)链路层广播标记(Link-level broadcast flag):FromDevice用于设置这个标志位,表示一个包是以链路广播的形式发送过来的,当发现这类数据包将要发送至其余接口时,IP路由器使用DropBroadcast的方式来丢弃这类包。

4)ICMP参数错误指针(ICMP Parameter Problem pointer):这个注释是由IPGWOptions设置的,用于声明该包的IP header字段中包含错误信息,并被ICMPError查看并用来生成ICMP错误信息。

5)修正IP源标记(Fix IP Source Flag):因为ICMP Error消息包的源地址必定是发生错误的接口,可是ICMPError并不能预测这个接口,所以它发送出的数据包在该字段中使用默认值并伴随一个Fix IP Source 的注释,用于提醒沿途的FixIPSrc添加正确的源地址并从新计算校验。

 

还有一种状况下,状态参数的传递并不局限于elements之间,而多是element须要获取到更为困难的全局变量,例如整个路由器在网络中的IP地址,或所处网络的广播地址,Click中的模块不少状况下必须掌握这些信息。每一个模块都须要掌管一个list来存储全部这些全局信息,更为理想的状况是能自动获取这个list(例如经过FBRC)。

对于原有IP标准的一致性同时体如今Click的element自己以及elements之间的顺序上。单个的element表现为:例如CheckIPHeader会检查IP头是否符合规范且合法;IPFragment会把大于MTU的包进行分块,并把没法分块的过大的包发送到报错的端口;ICMP error 消息不会对广播包、ICMP错误消息保等进行回复。Elements之间的顺序表现为:例如DecIPTTL被安排在LookupIPRoute以后,由于只有判断这个包不是发送给本身的时候才会把TTL减1。

 

4. Click拓展模

Click的转发路径上总共要通过16个模块,在这章中要展现的就是模块化带来的易于拓展的好处。

4.1 调度(scheduling)

所谓调度的意思就是让一个路由器在多个输入的状况下复用(multiplex)一个输出端口。Click中的scheduler表现为一个pull element,当这个调度器被要求发送出一个数据包时,会按照必定的算法每次从多个输入端口中选取一个端口的数据包并将其返回。目前主要实现的有三种调度器:RoundRobinSched,PrioSched和StrideSched。

RoundRobinSched顾名思义使用的就是RoundRobin规则(即轮询算法)来进行输入端口的选取;PrioSched是严格依照优先级进行调度,它始终优先传送优先级最高的输入端口,在一次传送次优先级的端口内容;StrideSched使用了stride scheduling 算法,简单来讲,stride scheduling就是一种按比例分配的算法,经过设定参数使得某个输入被轮到的频率是另外一个输入被轮到频率的n倍。

因为常规的队列(queue)和一个调度器(scheduler)只是在输入端口的选择上内部处理不一样,但对外表现都是同样的。具体来说,它们的输出端口都是pull类型的,所以下游element并不知道数据是从队列传来仍是从scheduler传来的。因此一个scheduler能够用做一个virtual queue,从而对外表现的像一个队列可是能够实现比FIFO更复杂的数据转发机制。

4.2 丢弃机制(dropping policies)

在一个Queue的element中每每会应用一个最简单的丢弃机制,即超出队列最大容量的全部包都会被丢弃,而另外一些policies可能会在Queue的这种机制基础上加以改进,如使用替换的方式。例如一个RED(Random Early Dection)的element可能会使用RED方法来drop过多的包,而这种dropping policy是独立于queue的数据存储功能的,只是在Queue基础上增长功能而已。

RED在检测到网络拥挤时有较大几率开始drop数据包。当一个队列中有不少包是发往同一链路时,该链路就被认为是拥挤的,此时RED模块就会查看路由器中各个队列的长度来决定是否要丢弃数据包,而在实际应用,RED模块每每就看下游最近的Storage element(Storage是全部具备存储功能的element向外提供的共有的接口,如queue或相似的模块都会具备Storage的接口),这种查看是经过FBRC完成的。

RED能够支持下游的多个队列(而不仅是查看一个)。若是RED下游有多个队列,返回的packet count会是全部队列模块的和(一个virtual count),这种机制可使得RED有不一样的变体:

a.例如把一个RED放在scheduler的两个队列以前,则会计算这两个队列总共的数据包。

b.另外一个例子是使用weighted RED,即packet会根据它们的优先级按不一样的几率进行丢弃。

c.第三种状况是RED放在了queue的后面(而不是前面),这时RED表现为一个pull element并会检测上游的Storage element(而不是下游的),从而应用相似于drop-from-front RED的策略。

 最简单的RED dropping功能能够经过在queue以前加RED模块来实现。

 

4.3 复杂拓展与简单分组

Click能够同时胜任简单或复杂的任务,一个网络管理员可让一个Click路由器完成特定的路由功能,也能够将Click路由器部分的element用于其余应用中。

4.4 区别化服务

在Click中,边界路由器和核心路由器能够共同提供区别化服务,管理聚合的数据流。边界路由器负责将不一样服务等级的数据包进行分类并打上标签,保证相应服务等级的数据流不会以太高的速率进入网络;核心路由器则经过这些标签来对流进行调度和队列管理。

区别化服务

上图就是一个区别化服务的具体应用框架。输入的数据包经过IP头部的DSCP(Diffierentiated Services Code Point)字段来划分为4个不一样的traffic streams。前三个(A,B,C)流是速率受控(rate limited)即提供保障的流,第四个(D)是提供最普通的尽力而为服务的数据流。具体来讲:A经过使用dropping机制来达到速率控制,既当每秒有超过7500个包到达时就会把多于的包丢弃;B是使用shaper进行整形,每秒容许10000个包经过shaper,多的包则存在前面的queue中;C是使用重分类(reclassification)机制,即超过设定数量的包会被从新标记为D类,降级进行尽力而为的传输。

4.5 以太网交换机

Click也能够用来构造非IP层的交换机,以下图中给出的就是一个符合IEEE 802.1d标准的以太网交换机。

以太网交换机

该交换机自身以学习网桥(learning bridge)的方式运行,并能够和其余的802.1d网桥一块儿在网络中运行生成树协议。在该交换机中,EthernetSwitch提供简单的学习网桥功能,EthernetSpanTree和Suppressor两个element则只是为了当LAN中存在多个交换机时防止环路的发生。其中EthernetSpanTree使用IEEE802.1d协议来为网络构建生成树,Suppressor用于控制将一个数据包从某一个特定的端口转发出去,同时Suppressor也对外提供方法接口来封锁或解锁一个特定的端口,全部到达Supressed的端口都会被丢弃(就是STP中的Block状态)。须要注意的是,Supressors没法经过FBRC来找到,所以使用者必须使用配置字符串(configuration string)使用名字来定位到它(在第二章提过)。

 

5. 内核环境

Click的核心线程是运行在linux2.2内核基础上的,核心线程启动路由器驱动并从而运行task queue中的多个任务。理论上只有中断才能够占据一个运行的线程,可是出于对系统交互性(responsive)的考虑,Click会主动每隔一段时间将线程控制权交还于Linux来执行一些系统级操做。通常来讲,在一个时间内只会有一个driver thread在运行,可是在thread初创建时可能会存在多个driver thread其中包含一个新线程和多个将要过时的老router thread。

Click运行在linux内核中,并使用linux的proc文件夹与用户进行交互。若是想要编写一个Click路由器,开发者能够将写好的代码放在proc/click/config文件夹中。当安装配置时,Click会在proc/click为每一个element建立子文件夹,其中包含了每一个element的handler(2.6中提到)。

一个Click路由器中包含有四种对象类别:

1) Elements: 系统为每个当前运行的element生成一个element对象。

2) Router: 一个router object会根据给定的router配置收集相关信息,包括配置elements,检查connections,将router上线,并管理任务队列。

3) Packets:Click的packet数据存储于内存块中,packet数据使用写时拷贝技术(copy-on-write),即当须要copy一个packet时,系统只会copy这个包的header而不会copy它的data。注释会以固定顺序存储于packet的header中,且没法动态增长新的注释。

4)Timers: 在Linux内核中,Click的timer经过Linux的timer queue来实现,在一个Intel PC上能够由0.1s的分辨率。

 

5.1Poll和设备处理

最原始的Click是和Linux共享中断机制和设备处理。虽然Click的宗旨仍是尽可能少的修改Linux,可是中断的开销和对于设备处理会很大程度影响performance,并且Click运行在较中断更低的优先级上,所以会致使“活锁“现象(livelock),即当input变多的时候,中断处理就会渐渐starve其余的操做,从而减小吞吐量。

如今的Click使用Polling代替了interrupt。用于设备处理的element(也就是FromDevice和ToDevice)将它们本身放在task queue中。Fromdevice会查看(poll)它的receive DMA queue是否有新到达的packets,若是有就依照configuration来push这些packet;ToDevice会检查它的device transmit DMA queue来看是否有空的slot,若是有的话就从它的input中pull出packets并装填进queue中。因为这种内部运做机制,两端的设备自己永远不会调用中断,从而使用Polling机制解决了中断问题。不过,虽然因为中断调用的过度昂贵,在低强度的工做环境下,传统的中断仍是被容许的。

因为这种Polling机制,Click会在原来的linux结构上有所调整,包括增长中断的开关控制,查看接收数据包,以及清理transmitted DMA ring.

相关文章
相关标签/搜索