Kea DHCP Hooks开发

来源:互联网 发布:绘声绘色软件怎么使用 编辑:程序博客网 时间:2024/06/06 02:29

目录

摘要
开发环境要求
Hook开发需求
Hook代码编写
Hook代码编译
Hook Kea配置
总结
参考文献
附录

摘要

简单Kea DHCP开发案例教程。通过本篇文章使读者能够对Hook开发有一个大致轮廓。主要侧重操作,对于概念的说明请查看官网。申明:本篇使用的是官方文档案例

开发环境要求

  1. Ubuntu
  2. gcc编译环境 apt install gcc
  3. vim编辑器
  4. Kea相关Hook开发库(安装Kea即可)

Hook开发需求

Kea DHCPv4用于分配IPv4地址给客户端(也会传给客户端其他信息,比如DNS服务器地址)。假设需要根据客户硬件地址和客户请求IPv4地址进行分类,且记录感兴趣的客户端硬件地址和已分配IP地址。

Hook代码编写

  • version - Hook代码被编译时Kea代码使用的版本
  • load - 当Hook被服务器加载时调用
  • unload - 当Hook被服务器卸载是调用

version函数

用于检查Hook是否兼容当前正在运行的Kea服务器。

源码

// version.cc#include <hooks/hooks.h>extern "C" {    int version() {        return (KEA_HOOKS_VERSION);    }}

load和unload函数

  1. load肯定被调用,unload不一定被调用,比如系统异常关闭。
  2. 这两个函数主要用于资源的分配和关闭。
  3. load函数可以注册自定义callout名字,比如如下代码:
int load(LibraryHandle& libhandle) {    // Register the callouts on the hooks. We assume that a header file declares the "classify" and "write_data" functions.    libhandle.registerCallout("pkt4_receive", classify);    libhandle.registerCallout("pkt4_send", write_data);    // Open the log file    interesting.open("/tmp/interesting.log", std::fstream::out | std::fstream::app);    return (interesting ? 0 : 1);}

源码

  1. 将创建两个文件,分别为library_common.h和load_unload.cc,library_common.h用于文件处理器声明。load_unload.cc用于定义load和unload函数。
// library_common.h#ifndef LIBRARY_COMMON_H#define LIBRARY_COMMON_H#include <fstream>// 日志文件处理器声明extern std::fstream interesting;#endif // LIBRARY_COMMON_H
  1. 定义load和unload函数源文件。==interesting.open的文件必须保证文件夹是存在的==,否则加载会失败
// load_unload.cc#include <hooks/hooks.h>#include "library_common.h"using namespace isc::hooks;// 日志文件处理器定义std::fstream interesting;extern "C" {    int load(LibraryHandle&) {        interesting.open("/tmp/interesting.log",std::fstream::out | std::fstream::app);        return (interesting ? 0 : 1);    }    int unload() {        if (interesting) {            interesting.close();        }        return (0);    }}

Callout函数

这里的Callout是所有Hook point的统称。并不是真的叫callout这个名字。

callout签名

返回0表示成功,非0表示失败。

extern "C" {    int callout(CalloutHandle& handle);}

callout参数

CalloutHandle对象提供2个方法getArgument和setArgument,获取和设置参数。下面的举例如何使用

// 服务端代码片段,演示设置参数int count = 10;boost::shared_ptr<Pkt4> pktptr = ... // Set to appropriate value// 假设handle对象已经创建handle.setArgument("data_count", count);handle.setArgument("inpacket", pktptr);// 调用 callouts...// 获取修改的值handle.getArgument("data_count", count);handle.getArgument("inpacket", pktptr);

源码

// pkt4_receive.cc#include <hooks/hooks.h>#include <dhcp/pkt4.h>#include "library_common.h"#include <string>using namespace isc::dhcp;using namespace isc::hooks;using namespace std;extern "C" {// 在pkt4_receive时调用该函数int pkt4_receive(CalloutHandle& handle) {    // A pointer to the packet is passed to the callout via a "boost" smart    // pointer. The include file "pkt4.h" typedefs a pointer to the Pkt4    // object as Pkt4Ptr.  Retrieve a pointer to the object.    Pkt4Ptr query4_ptr;    handle.getArgument("query4", query4_ptr);    // Point to the hardware address.    HWAddrPtr hwaddr_ptr = query4_ptr->getHWAddr();    // The hardware address is held in a public member variable. We'll classify    // it as interesting if the sum of all the bytes in it is divisible by 4.    //  (This is a contrived example after all!)    long sum = 0;    for (int i = 0; i < hwaddr_ptr->hwaddr_.size(); ++i) {        sum += hwaddr_ptr->hwaddr_[i];    }    // Classify it.    if (sum % 4 == 0) {        // Store the text form of the hardware address in the context to pass        // to the next callout.        string hwaddr = hwaddr_ptr->toText();        handle.setContext("hwaddr", hwaddr);    }    return (0);};}
// pkt4_send.cc#include <hooks/hooks.h>#include <dhcp/pkt4.h>#include "library_common.h"#include <string>using namespace isc::dhcp;using namespace isc::hooks;using namespace std;extern "C" {// 在pkt4_send hook的时候调用int pkt4_send(CalloutHandle& handle) {    // Obtain the hardware address of the "interesting" client.  We have to    // use a try...catch block here because if the client was not interesting,    // no information would be set and getArgument would thrown an exception.    string hwaddr;    try {        handle.getContext("hwaddr", hwaddr);        // getContext didn't throw so the client is interesting.  Get a pointer        // to the reply.        Pkt4Ptr response4_ptr;        handle.getArgument("response4", response4_ptr);        // Get the string form of the IP address.        string ipaddr = response4_ptr->getYiaddr().toText();        // Write the information to the log file.        interesting << hwaddr << " " << ipaddr << "\n";        // ... and to guard against a crash, we'll flush the output stream.        flush(interesting);    } catch (const NoSuchCalloutContext&) {        // No such element in the per-request context with the name "hwaddr".        // This means that the request was not an interesting, so do nothing        // and dismiss the exception.     }    return (0);}}

Hook代码编译

根据上面的操作,完整的文件列表如下:

source-code-list

编译

g++ -I <install-dir>/include/kea -L <install-dir>/lib -fpic -shared -o example.so load_unload.cc pkt4_receive.cc pkt4_send.cc version.cc -lkea-dhcpsrv -lkea-dhcp++ -lkea-hooks -lkea-log -lkea-util -lkea-exceptions

上面编译成功后会在编译目录下,看到 example.so

指向kea安装时的目录,除非你安装时指定–prefix,否则默认为/usr/local

选项 解释 -I 指定额外的头文件搜索路径DIRECTORY。 -L 指定额外的函数库搜索路径DIRECTORY -fpic 表明使用地址无关代码,PIC:Position Independent Code. -shared 生成共享目标文件 -o 指定生成库的名字 -l 连接时搜索指定的函数库LIBRARY

Hook Kea配置

复制

复制Hook库到/usr/local/lib/hooks/下(根据你的喜好你可以放置在任意位置)

编辑

编辑vim /usr/local/etc/kea/kea-dhcp4.conf,在文件的Dhcp4节点下添加如下配置

"hooks-libraries": [{    "library" : "/usr/local/lib/hooks/example.so"}]

重启

重启后,运行测试工具,成功的话可以在/tmp/interesting.log目录如下输出:

filter_result

总结

Kea如何测试测试,请查阅Karaf教程之Config Admin服务的使用。

参考文献

Hooks Developer’s Guide

Hooks Libraries

Linux共享对象之编译参数fPIC


myqrcode

附录

源码附件

原创粉丝点击