这篇文章主要总结一些模块开发的基础知识。
引文是为FreeRadius3.X增加一个模块的帮助文档,在这里做一些翻译。
doc/developer
给开发者的文档,对与模块开发的说明在这里:
https://github.com/FreeRADIUS/freeradius-server/blob/v4.0.x/doc/developer/module_interface.rst
服务器的职责
这个文档首先说明了Freeradius服务器主要工作。Freeradius服务器就是一个认证服务器(AAA服务器,认证、授权、计帐),它只做如下工作:
- 获取一个RADIUS request
- 处理该request
- 在database中查找信息
- 将信息存储在database中
- 返回一个response
所以Freerasdius不需定时器等其他复杂的设计,因为那样会让服务器不稳定、不安全、不易维护。(KISS原则即视感)
一些思考:看到这里感觉FR就是在TCP/IP的基础上,进行分包的处理:接收一个请求request包;分析这个包;处理逻辑;返回响应response。
服务器的业务逻辑重点就是分包的处理。底层的IO由网络库来支持好,那么程序员的工作效率会有所提高吧。muduo网络库、handy网络库是C++网络库,libevent是C网络库,应该感谢这些作者。
模块简介
一个模块由若干组件构成:
- authorization(授权):检查用户是否存在,确定一个认证过程,设置一些response中的attribute
- authentication(认证):验证密码是否正确
- accounting(计帐): 将request记录在log中
…
A module declares which components it supports by putting function pointers in its “module_dl_t rlm_*” structure.
组件其实就是函数,也就是说一个模块需要实现一些特定的函数,供FR调用。这些函数的指针存放在一个叫modult_t
的结构中。
模块的配置
要想使用某个模块,需要在radiusd.conf中的modules{}块中添加该模块的实例:
module_name [instance_name] {
param1 = value1
param2 = value2
param3 = value3
...
}
module_name可以在安装目录下的lib文件夹中查找,我的安装目录是/usr/local,因此/usr/local/lib
下包含所有安装的模块,名字为rlm_xxx.so
instance_name用于区分同一个module的不同实例,如果只有一个实例可以忽略不写。
param是模块定义的参数,通常是指定database的位置、开启某些功能。它们由服务器直接传递给模块。
当服务器接收到一个Access-Request时,authorize{} 块被调用(它会选定某种Auth-Type),然后authenticate{}中的对应的Auth-Type{}块被调用。最后post-auth{}块被调用。
模块的生命周期
当服务器启动时或接收到SIGHUP而重新初始化时,服务器会读取modules{}块,在该块中的module会被加载,并且它的init()方法会被调用。(没明白init)
接着module的instantiate()被调用,server会给该函数传递两个参数:
- CONF_SECTION *cs:通过cs可以访问到在前面设置的模块参数等。
- void *instance : 该模块实例的指针。
int instantiate(CONF_SECTION *cs, void *instance)
模块的authorize(), authenticate(), preaccounting(), and accounting()也以同样的方式被调用,它们的原型如下所示:
int authorize(void *instance, REQUEST *request)
int authenticate(void *instance, REQUEST *request)
int preaccounting(void *instance, REQUEST *request)
int accounting(void *instance, REQUEST *request)
instance与上面相同,是模块的handle,request则代表请求。它们会使用instance中保存的database来处理该request。
当服务器关闭时,模块的detach被调用,它会释放在instantiate中获取的资源。当一个模块的所有实例被detached了,模块的destroy被调用,它释放在init中获取的资源。
src/modules/stable
将模块名字添加到这个文件中,模块会被编译。
服务器处理流程
FreeRADIUS uses a thread pool to serve requests. Each request is processed
synchronously, and processing passes through a series of stages, and a list
of modules in each stage.
The request is processed as follows
- The radius packet is received by a listener - see listen.c
- The radius packet is parsed and validated into a request - see ?
- The request is processed - see process.c
- The server passes through each authentication stage
- authorize
- if Proxy-To-Realm is set:
- pre-proxy
- send proxy request
- post-proxy
- else
- authenticate
- post-auth
- Authentication stages are lists of modules - see modcall.c