click模块路由--介绍 & 快速开始

作者 Lao Yuan, 发布于 Lao-Yuan's Blog , 禁止转载.

信安竞赛结束一段时间了,闲下来整理一下项目中用到的 click 模块路由框架的相关内容(没得奖就只能做这种工作).由于 click 这个东西的中文资料实在没多少(事实上英文资料除了官方 wiki 也不多),写一下我对该工具的理解和上手时遇到的坑,以方便初学 click 的读者快速上手.

主要的资料来源是 click wiki ,这里有 click 安装使用方式以及几乎所有包的资料,页面右边的 search 功能在查找特定包的描述时非常有用.

学术性的资料有click最初的 click paper ,主要是此软件的设计思路和性能评估.

最后还有 alan-mushi 使用 click 框架编写的 LISP 协议项目的 Blog:[Click Modular Router] Protocol implementation tutorial , 在最开始学习时提供了很大帮助(因为很少有这么简单而且清晰的 click 实战项目).

关于click

What – 什么是click模块路由

click 是一个由 MIT 的 Eddie Kohler 教授带领开发的非常实用的”模块化数据处理和分析”框架,该框架运行于linux平台,使用该框架,我们可以轻松搭建一个工作在OSI 模型链路层,网络层以及传输层的的网络应用. 该框架使用简单,只需要使用click框架自定义的简单的配置语言就可以对网络流量进行过程化的处理; 同时由于其”模块化”的特点, 也有着高度的可扩展性和优秀的自定义性能. 对于有自定义网络功能的需求(如防火墙, 路由, 内容过滤)是很好的选择.

从目前的技术概念来看, click 框架是一种基于 NFV 框架(即网络功能虚拟化,Network Function Virtualization, 通过使用 x86 等通用性硬件以及虚拟化技术,来承载很多功能的软件处理)的软件设计. 但事实上,click 是于 2000年左右开发的软件, 远早于NFV提出的2012年, 也可以看出 Kohler 也是非常的有前瞻思维.

然而,正如前文所提到的,要使用 click 框架, 如何写一份满足需求配置文件是使用者关注的主要问题,我们必须学习 1) 配置文件的语法规则; 2) 每个 element 的使用方法; 3)计算机网络的相关知识. 因此,学习成本高是使用者遇到的主要问题.

Why – 为什么用这个框架

click 目前托管于 github, 有400+ start, 项目文档比较完善. 事实上,这是一个很老的项目(2000年),相对来说是一个很小众的工具,然而对于小工程来说,click 又是一个非常好的框架,很少能够找到同样功能和特点且可用的替代工具. click 作为一个硬件虚拟化的产品,有如下几个特点:

高可用性

click 可以完成对链路层,网络层,传输层的全部包处理. 使用click,你可以从一条流量被网卡设备输出开始对数据包进行操作, 获取帧信息和帧内数据,最后到数据被传入系统内核; 对每一层协议进行操作,从 mac 帧到 ip 帧 再到 tcp 帧, 满足开发者在开发网络应用时的各种需求.

click 是模块化的, 它将网络的一些最小功能抽象成一个个 element ,提供最小的功能集;每个 element 作为顶点点, 多个 element 间的拓扑关系作为边, 将开发者要对数据包的处理抽象成一个有向图. 使用此框架类似于图的动态组装和功能的重构, 开发者将要处理的需求问题细分为每个 element 的工作,再将各个 element “拼装” 起来,完成自己的网络应用的开发.

高度可定制

如上文所说, click 将数据包的处理抽象成了一个个 element, 但如果开发者想使用的功能并没有被 click 预先包含该如何组成自己的应用? click 具有高度的可定制性, 允许开发者自行编写自己的 element.

你可以认为一个 element 是一个类, 实现了一些包处理过程中的方法. 与程序设计语言不同的是,用户不能主动调用这个类或使用里面的方法, click 框架在运行时会自动循环运行类中的一些方法,等待外界输入或向外界请求一个数据包.

通过遵守 click 的 element 编写规范,我们可以自定义一些 element, 然而事实上,大部分的网络功能已经由 click 开发人员实现过了, 我们要做的只是去”组装”这些 element.

高性能

由于 click 的开发时间较为久远, 官方给出的 ip router 进行转发操作的测试结果如图所示:

click测试效果

click测试效果

测试的环境是 9 台当时性能最高的 Pentium III 处理器, 其中一台部署了 click router,四台作为发送设备发送UDP数据包,四台作为接受设备,可以看出,click 的最高稳定工作的吞吐量远高于直接有 linux 操作系统作为路由设备, 也高于 polling linux(一种由进程轮询设备进行网络操作的 linux 网络处理方法). 同时, linux paper 表示其在测试环境中处理速度达到了每秒 357,000 个 64-byte 包, 也即约 174.3 mbps 的处理速度, 这个速度在今天也是完全可以作为末端路由来使用的, 且只是使用 Pentium III 这种远古处理器.

click 完全可以满足一般小型企业或个人的防火墙,路由等需求,高性能是我们乐意使用 click 的一个方面.

ps. 一些 element 的使用会大幅度降低网络 IO 性能, 尤其是一些 IO 操作相关的 element.

How – 快速开始

click 有两种运行方式: user 模式, 即作为一个 用户态进程存在; 或内核模式, 即作为一个内核模块,在系统加载时运行. 理论上内核态的 click 会有更加好的性能表现.

然而,内核态运行 click 要求的 linux 内核版本是 2.6 版本, 版本过低导致除了 click 几乎不能运行其它任何服务, 且内核态的 click 安装和使用也比较复杂,因此我们使用 ubuntu 16.04 作为本文的测试操作系统,仅作用户态的 click 测试.

ps.最新的 ubuntu 18.04 在运行 click 时会出现错误,这可能是因为 click 没有及时更新的缘故, ubuntu 18.04 的网络环境产生了一些变化.

安装

在 click 的 github 项目地址找到所有关于此项目的内容.查看 INSTALL.md 获取有关编译的具体信息.

需要最新版的 gcc 和 g++ 编译器.

$ git clone https://github.com/kohler/click
$ cd click
$ ./cofigure
$ make
# 安装到系统变量中,否则可以直接在安装目录下运行 ./click
$ sudo make install

运行

现在我们找到一个最简单的 click config file 来使用 click 框架.在 click 的源代码中,我们可以找到一些以 .click 结尾的文件, 我们以 conf/test.click 为例:

$ cd conf
$ click test.click
ok:   40 | 45000028 00000000 401177c3 01000001 02000002 13691369
ok:   40 | 45000028 00000000 401177c3 01000001 02000002 13691369
ok:   40 | 45000028 00000000 401177c3 01000001 02000002 13691369
ok:   40 | 45000028 00000000 401177c3 01000001 02000002 13691369
ok:   40 | 45000028 00000000 401177c3 01000001 02000002 13691369

OK! 我们现在已经运行了一个最简单的 click, 它向屏幕输出了一些字符. 当然这个 test 并没有牵扯到网络的内容. 为了测试网络相关的内容, 根据此文件下的 demo.click (该文件有一些小错误,可能是工作于内核模式的 click 测试导致的),我们可以创建另一个如下的配置文件 mydemo.click :

// 获取包数据
FromDevice(ens33) -> c0 :: Classifier(12/0806 20/0001,
                                     12/0806 20/0002,
                                     12/0800,
                                     -);

out :: Queue(1024) -> ToDevice(ens33);

// ARP请求,需要被响应回复
c0[0]
    -> ARPResponder(192.168.2.140 00:0c:29:fe:0c:36)
    -> out;

// ARP回复被传递给 Linux
c0[1]
    -> out;

// 非 IP 包被过滤
c0[3]
    -> Discard;

// 其他均为ip包, 按udp,tcp,其他分类
c0[2]
    -> Strip(14)
    -> CheckIPHeader()
    -> c1 :: IPClassifier(udp,tcp,-);


// TCP 包
c1[1] -> Print(TCP) -> out;

// UDP 包
c1[0]
    -> Print(UDP)
    -> out;

// 既不是udp也不是tcp的ip包
c1[2] -> Print(UNKNOWN) -> out;

这个demo的作用是输出当前你的一个网卡接受到的数据类型,并选择处理他们的方式.但是由于 click 并非完全自动化的框架,我们需要在使用一些 element 时注明一些硬件相关的信息,如在这份 demo 中就用到了网卡名称 网卡MAC 此网卡IP 等信息.

要获得自己机器的硬件信息,我们使用

$ ifconfig

的方式获得我们需要的网卡信息,并更改一些需要用到这些硬件信息的 element. 这时, 这个文件即可被用于配置 click. 需要注意的是,由于 click 的 FromDevice 这个 element 直接读取硬件,因此需要 root 权限.

$ sudo click mydemo.click
UDP:   40 | 45000028 e4820000 800635fb 342b28f3 c0a8028c 01bbb9c0
TCP:  185 | 450000b9 e4830000 8011cfd1 c0a80202 c0a8028c 0035c324
TCP:  121 | 45000079 e4840000 8011d010 c0a80202 c0a8028c 0035c324
UDP:   40 | 45000028 e4850000 800630e6 7512ed1d c0a8028c 0050c18a

这时通过你设置的网卡进行通信时, click 读取到了所有的包并分析其类型,如 ICMP, TCP, UDP 等内容并打印到屏幕,之后的数据是该包的 MAC 帧头部.

同时, 这个 demo 也实现了自动 ARP 回应.

Done! click 框架被成功运行在 userlevel.

设计理念

click 被设计解决一个问题: 如何进行用于包处理(packet-processing)的软件开发.

包处理包含两个主要的部分: 主机(hosts) 和 路由器(router), 其中, 主机负责一个包(packet)的产生到结束, 它关心一个包的具体内容; 而路由则不同, 它只关心包应该如何被传递, 这就代表 router 负责查看每个包的头部信息, 并决定这个包如何传递: 是否传递, 接受方是谁, 如何构造包的头部让其正常工作. 主机和路由让整个计算机网络可以正常工作于网络协议上.

由于协议只考虑正常连接问题,我们需要一些运行于网络上的必不可少的其他应用,如防火墙,地址转换器(eg.NAT),负载均衡等一些其他应用,他们也都是包处理程序, 甚至可以被认为是高可用性路由器的一部分.

但这些应用对开发者的要求更高. 通常情况下, 当开发者开发这一类复杂而灵活的软件时, 要深入编程每一个字节来保证自己的网络应用可以正常使用, 这导致这类的开发十分的复杂和抽象, 也十分难以进行维护. 网络管理者只能确定关闭或开启一个 router, 却很难具体的解释或操作其中的没一个功能.

click 被设计用于开发更灵活, 模块化, 易于维护的网络应用.

基本特点

click 模块路由是由一系列名叫 element 的高细粒度的软件组件构成. 这些 element 用于处理 packets.

element

simple element

click 由一个配置文件驱动. 当构造一个 click 配置文件时, 用户可以选择一些列 elemnts, 并把它们连接成一个有向图. 这个有向图的含义是一个 packet 在各个 elements 之间的传输过程.

click 如此设计的优点如下:

  • 灵活多变的抽象方法

    click 抽象并只抽象了一个概念: element. 这样设计的直接后果是用户会倾向于将整个包处理进行分割, 直到其成为一个个 element 大小的处理碎片. click 是一个模块化的软件, 而且其模块化的设计也将影响使用者的使用策略—-更注重系统的模块化,这使得系统方便进行维护.

  • 配置语言

    click 的配置由一系列 elements 组合而成. 当设计一个新的配置时, 我们可以独立编写一个新的独立的 element, 这一部分使用 C++ 编写并可编译到 click 软件中.当在连接已经写好编译过的 elements 时,会用到一种完全自创的 click language. click 语言是需要完全声明的, 这使得配置文件即可以是 human-readable 的,也可以被由计算机自动编辑.

  • 没有限制

    虽然 click 的 element 和 clcik language 限制了使用者实现功能的方法,但却完全没有限制 click 框架可以完成的任务范围. 虽然现有的 element 都使用了相同的数据传输机制, 但开发者完全可以再创建一个新的传输机制. 任何潜在的使用方式都不会被 click 的工作所限制.

结语

本文介绍了 click 框架的基本用法和快速开始, 然而一份 config 文件如何编写, 如何编写自己的 element 我们仍然没有涉及. 由于 click 的element 数量众多, 设计到计算机网络的各个功能和细节, 我们并不能详细的说明每个 element 的使用方法,但当我们上手一个 element 时, 最简单也是最快捷的方法就是在 click wiki中阅读它的文档. 因此一定要强调的是, 该 wiki 中有 click 的各种具体用法, 是学习 click 的一手资料.如果对 click 的设计方法和技术细节有疑问,也可以查阅 click paper.

上手 click 离不开简单而实用的应用实例, 在之后的文章中会展示几个简单的 click demo, 包括配置和新建 element, 以方便快速上手.

对于本文有任何问题,请务必联系我.