linux 下的 iptables/ netfilter 防火墙 深度理解 中篇

一 概述

前篇主要提到了用户空间 iptables 1.3.5 源码对规则的处理。但是并没有涉及内核空间netfliter 模块的处理。用户空间上的规则要生效最终肯定是通过传给内核空间的netfilter,让netfliter这个老大哥处理。因此抛出两个问题。

  • 用户空间的是怎么获取内核空间已经存在的规则,或者用户空间是如何将需要netfilter处理的规则下发给内核?简而言之,规则在用户空间是如何跟内核空间交互的。
  • netfilter 内核收到用户空间的请求,规则在内核是怎么处理的?最终又是怎么让规则起作用,达到防火墙预想的功能呢?

本篇主要解决这两个问题

二 iptables 用户空间 与 netfilter 内核空间规则的交互

本章深入的内核版本是比较新的4.18.15 版本。
在说明内核,用户间如何交互时,前篇有张图其实很清楚了展示了交互的过程,如下图所示
netfilter 与 iptables 关系
这幅图,很直观的反应了用户空间的iptables和内核空间的基于Netfilter的ip_tables模块之间的关系和其通讯方式,以及Netfilter在这其中所扮演的角色。
从图中可以看出iptables 是通过setsockopt,getsockopt 系统调用获取传递规则链的。
那么iptables 1.3.5 源码是真的这么实现的吗?前篇似乎并没有看到这两个系统调用。
其实不然,iptables 操作规则跟内核交互都交给了libiptc.so库实现了。

1.用户层的交互处理

libiptc.so 库里调用的iptc_init() ,iptc_commit() 里面分配调用了setsockopt,getsockopt
iptc_init() 函数

iptc_handle_t iptc_init(const char *tablename);
#define TC_INIT        iptc_init //宏定义
#define TC_HANDLE_T  iptc_handle_t  //宏定义
TC_HANDLE_T
TC_INIT(const char *tablename)
{TC_HANDLE_T h;STRUCT_GETINFO info;unsigned int tmp;socklen_t s;iptc_fn = TC_INIT;if (strlen(tablename) >= TABLE_MAXNAMELEN) {errno = EINVAL;return NULL;}if (sockfd_use == 0) {sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);if (sockfd < 0)return NULL;}sockfd_use++;s = sizeof(info);/*----------------------关键部分一 start-----------------------*/strcpy(info.name, tablename);if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {if (--sockfd_use == 0) {close(sockfd);sockfd = -1;}return NULL;}/*---------------------------end------------------------------*/DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",info.valid_hooks, info.num_entries, info.size);if ((h = alloc_handle(info.name, info.size, info.num_entries))== NULL) {if (--sockfd_use == 0) {close(sockfd);sockfd = -1;}return NULL;}/* Initialize current state *//*----------------------关键部分二start-----------------------*/h->info = info;h->entries->size = h->info.size;tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,&tmp) < 0)goto error;/*-----------------------end--------------------------------*/
#ifdef IPTC_DEBUG2{int fd = open("/tmp/libiptc-so_get_entries.blob", O_CREAT|O_WRONLY);if (fd >= 0) {write(fd, h->entries, tmp);close(fd);}}
#endifif (parse_table(h) < 0)goto error;CHECK(h);return h;
error:if (--sockfd_use == 0) {close(sockfd);sockfd = -1;}TC_FREE(&h);return NULL;
}

接下来分析代码中标志的关键部分
关键部分一:
strcut STRUCT_GETINFO info;
getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s);
根据info.name 告知内核需要那张表的规则信息。然后内核会返回这张表内规则的想关信息。例如这张表管理了几条链,这张表有多少条规则,规则的占用的空间大小等等
关键部分二:
getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries, &tmp) < 0
再根据SO_GET_INFO获取的表内规则的信息,内核会再把这表上的具体规则数据返回给用户。

iptc_commit() 函数 (本函数比较长,只列出了关键代码)

int iptc_commit(iptc_handle_t *handle);
#define TC_COMMIT   iptc_commit//宏定义
int
TC_COMMIT(TC_HANDLE_T *handle)
{...ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,sizeof(*repl) + repl->size);if (ret < 0) {errno = ret;goto out_free_newcounters;}...ret = setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,newcounters, counterlen);if (ret < 0) {errno = ret;goto out_free_newcounters;}...

本函数大部分都在填充要传递到内核的STRUCT_REPLACE *repl;STRUCT_COUNTERS_INFO *newcounters;两个结构体,然后下发到内核。
STRUCT_REPLACE *repl 后面内核的时候会做详细讲解,保存着链上的规则。
而newcounters 结构体是干嘛用的呢?每条规则entry都一个计数器,用来记录该规则处理了多少数据包。
至此用户层如何跟内核交互已经了解了大概。

  • setsockopt新增命令字:
  1. #define SO_SET_REPLACE//设置规则

  2. #define SO_SET_ADD_COUNTERS //加入计数器

  • getsockopt新增命令字;
  1. #define SO_GET_INFO //获取ipt_info

  2. #define SO_GET_ENTRIES //获取规则

但是内核层又是怎么处理用户层的请求的呢?

2 内核层的交互处理


上图是之前大神总结的用户层调用setsockopt 系统调用时内核层是如何一步一步处理的。本人也跟着上图步骤验证过。

// socket.c 文件中
static int __sys_setsockopt(int fd, int level, int optname,char __user *optval, int optlen)
{int err, fput_needed;struct socket *sock;if (optlen < 0)return -EINVAL;sock = sockfd_lookup_light(fd, &err, &fput_needed);if (sock !&


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部