由于语言需求,需要使用go实现libipset的功能,与内核通信,就简单的看了一下源码(•̀⌄•́)
—— By Jihan
本文主要根据ipset 7.x版本来进行介绍的。
简单介绍
简介
ipset是 Linux 防火墙 iptables 的一个协助工具。 通过这个工具可以轻松愉快地屏蔽一组IP地址。–来自wiki
ipset主要解决的是iptables在屏蔽大量ip产生的效率低下问题。搞一张图看看效率差距:
–测评来源
并且,iptables在进行规则插入和删除的时候,也只能一条条的进行,速度也是非常慢的。我自己在自己的设备上测试效果如下(time + 脚本测试的):
添加Iptables数量 | 1 | 500 | 1000 | 2000 | 4000 | 10000 |
---|---|---|---|---|---|---|
花费时间 | 0.002s | 2.831s | 4.115s | 10.725s | 33.365s | 2m55.954s |
那么,如果你有需求使用iptables屏蔽大量的ip,就可以考虑使用ipset。
使用
看源码之前,首先需要了解ipset有什么用,简单给个示例来屏蔽一个ip:
你要有两台机器,可以是自己的虚拟机,在一台机器上配置ipset + iptables,另外一台机器去ping测试。
安装(centos):yum install ipset
1 | ipset create hash_test hash:ip #创建一个集合 |
你也可以进行更多的尝试,参见官网
ipset的使用不是本文主要目的,简单给个示例就行了。
基本流程
libipset属于用户态部分的代码,负责与内核通信,真正的ipset工作的地方是在内核的netfilter中。
我们就根据上面ipset create hash_test hash:ip
命令来分析大概的流程。
特殊说明在流程图里都有备注,顺便说明一下几个固定列表的位置:
- 各种类型的类型列表,包含所支持的命令,以及需要的参数:ipset_<type_name>.c
- 错误码:errcode.c
- 输入命令参数列表(主要是-s这种类型的参数):ipset.c
- 输入命令参数列表(主要是add这种类型的参数):args.c
- 某个命令的消息协议:PROTOCOL
消息格式
ipset使用的消息是Netlink通信,在ipset用户态构造的sock参数:AF_NETLINK,SOCK_RAW,NETLINK_NETFILTER,sock具体用法自己去查。ipset用户态构造的sock需要进行bind才能使用,因为它不能像udp这种自动分配发送端的端口。
首先来看看netlink的消息格式(主要参考):
struct sockaddr_nl
结构:
1 | struct sockaddr_nl { |
struct nlmsghd
结构:
1 | /* struct nlmsghd 是netlink消息头*/ |
通常这个头消息的构造,在ipset中是下面的代码(其中seq是一个自增检验数,pid通常设置0):
1 | static void |
struct msghdr
结构:
1 | struct iovec { /* Scatter/gather array items */ |
这个结构体主要在ipset接收消息的时候使用。具体代码包含在libmnl库中,需要下载源码。然后就可以看到以下的接收消息的函数:
1 | ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, |
上述的netlink消息数据结构,也就是ipset里使用的主要结构,而这些消息结构的关系如下:
图片来源
上述示例的就是ipset中使用到的主要消息结构了,更多细节,还是在源码中查看。
示例Demo
这里我们列出两个语言的demo,C和Go的。
C
c的相对简单,因为只需要调用libipset提供的接口就行。这里推荐写法和官方的ipset的main函数写法一致。
官方main函数(这个函数只有在ipset 7版本中才有)
1 |
|
是不是简单到爆。
第二种,是使用libipset中的session结构体(参考来源):
1 |
|
编译(根据各自的环境调整):
1 | gcc -g -O2 -Wall -Werror -c ipset_test.c -o ipset_test.o |
GO
go有三种方式来调用ipset,第一种是用执行命令的方式,第二种是使用cgo的方式,第三种是使用netlink通信的方式。这里简单给下第二种和第三种的demo:
cgo:
ipset.go:
1 | package main |
cipset.h
1 |
|
cipset.c 和上面的C第二种示例代码一致,这里就不占空间了。
编译执行:
1 | gcc -g -O2 -Wall -Werror -rdynamic -fPIC -shared -o ./libcipset.so ./cipset.c #动态库生成 |
goipset实现:
我已经初步用golang实现了ipset,详情可我的开源:https://github.com/JiHanHuang/goipset
以上,如果有什么问题,欢迎随时交流。-(¬∀¬)σ