计算机系统应用教程网站

网站首页 > 技术文章 正文

理解 Linux 下的 Netfilter/iptables

btikc 2024-10-12 11:07:52 技术文章 19 ℃ 0 评论

Netfilter/iptables 项目由 Rusty Russe 创建于1998年,并于 1999 年建立了 Netfilter Core team,并在此后负责维护此项目,同时也于2000年3月合并进了 linux 2.3.x 版本的 linux 内核。

Netfilter/iptables 有三部分组成,分别是Netfilter 框架/Iptables(内核空间)/Iptables 命令行工具(用户空间)。

Netfilter 是一个由Linux 内核提供的框架,可以进行多种网络相关的自定义操作。

例如:

  • 无状态的报过滤(IPv4 and IPv6)
  • 有状态的报过滤(IPv4 and IPv6)
  • 网络地址转换(NAT/NAPT)

Netfilter 在 Linux 内核中表现为一系列的hook, 并允许Linux 内核模块注册为回调函数,Linux内核模块通过回调函数操作网络报文。

架构

Netfilterk框架是如何工作的?

Netfilter 一共提供了5个hook,分别位于linux 网络栈中的各个处理节点,如下图:

--->[NF_IP_PRE_ROUTING]--->[ROUTE]--->[NF_IP_FORWARD]--->[NF_IP_POST_ROUTING]--->
                              |                        ^
                              |                        |
                              |                     [ROUTE]
                              v                        |
                       [NF_IP_LOCAL_IN]        [NF_IP_LOCAL_OUT]
                              |                        ^
                              |                        |
                              v                        |

Netfilter Hook的意义:

  • NF_IP_PRE_ROUTING: 位于路由之前,报文一致性检查之后(报文一致性检查包括: 报文版本、报文长度和checksum)。
  • NF_IP_LOCAL_IN: 位于报文经过路由之后,并且目的是本机的。
  • NF_IP_FORWARD:位于在报文路由之后,目的地非本机的。
  • NF_IP_LOCAL_OUT: 由本机发出去的报文,并且在路由之前。
  • NF_IP_POST_ROUTING: 所有即将离开本机的报文。

Linux 内核模块可以注册到任何的hook,注册的回调函数也必需指定优先级。当一个报文通过hook的时候,hook将会依据优先级调用回调函数。注册的回调函数,可以有五种返回,每种返回代表对报文不同的操作:

  • NF_ACCEPT: 继续正常处理此报文,即允许报文通过。
  • NF_DROP: 丢弃此报文,不再进行继续处理,即拒绝此报文。
  • NF_STOLEN: 取走这个报文,不再继续处理。
  • NF_QUEUE: 报文进行重新排队,可以将报文发到用户空间的程序,进行修改或者决定是拒绝或者允许。
  • NF_REPEAT: 报文重新调用hook。

什么是内核空间的iptables?

Iptables 是基于Netfilter框架实现的报文选择系统,其可以用于报文的过滤、网络地址转换和报文修改等功能。Iptables 本质上是包含了5个规则表,而规则表则包含了一些列的报文的匹配规则以及操作目标。以下对每个规则表进行了简单说明:

  • Filter Table: 是一个默认的规则表,用于报文的过滤。他注册了三个链: INPUT、FORWARD和OUTPUT。
  • NAT Table: 主要用于NAT转换,注册了四个链:PREROUTING、INPUT、OUTPUT和POSTROUTING。
  • Mangle Table: 主要用于报文的修改,一共注册了五个链: PREROUTING、OUTPUT、INPUT、FORWARD和POSTROUTING
  • Raw Table: 可以对报文不进行链路跟踪,其优先级在hook中注册很高,注册了两个链: PREROUTING和OUTPUT
  • Security Table: 用于强制网络接入控制,注册了三个链:INPUT、OUTPUT 和 FORWARD

操作目标指的是否允许报文通过,如果允许即为ACCEPT,拒绝则为DROP。

如何使用iptables命令行工具?

iptables命令行工具是工作在用户空间的操作工具,用于修改内核空间的规则表,下面给出了几个例子:

拒绝来自192.168.12.4的报文

# Drop all incoming packets from address 192.168.12.4
iptables -I INPUT -s 192.168.12.4 -j DROP

拒绝ping任何主机

iptables -A OUTPUT -p icmp -j DROP

以下为来自man iptables的使用方法

# Append rule-specification in selected chain
iptables [-t table] -[A] chain rule-specification [options]

# Insert one or more rules in the selected chain as the given rule number.
iptables [-t table] -I chain [rulenum] rule-specification [options]

# Replace a rule in the selected chain
iptables [-t table] -R chain rulenum rule-specification [options]

# Delete one or more rules from the selected chain.
iptables [-t table] -D chain rulenum [options]

# List all rules in the selected chain.
iptables [-t table] -[L] [chain] [options]

# Flush the selected chain.
iptables [-t table] -[F] [chain] [options]

# Set the policy for the chain to the given target.
iptables [-t table] -P chain target [options]

动手编写一个netfilter hook回调函数

以下代码实现了一个简单的linux 内核模块,并且通过netfilter hook注册了一个回调函数,以此来实现禁用ICMP报文(无法从本机ping任何主机)。

/*
 * This code was compiled and tested on Ubuntu 18.04.2
 * with kernel version 4.15.0
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rui");
MODULE_DESCRIPTION("A simple example netfilter module.");
MODULE_VERSION("0.0.1");

static struct nf_hook_ops *nfho = NULL;

static unsigned int hfunc(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
  struct iphdr *iph;
  if (!skb)
    return NF_ACCEPT;

  iph = ip_hdr(skb);
  if (iph->protocol == IPPROTO_ICMP) {
    return NF_DROP;
  }

  return NF_ACCEPT;
}

static int __init LKM_init(void)
{
  nfho = (struct nf_hook_ops*)kcalloc(1, sizeof(struct nf_hook_ops), GFP_KERNEL);

  /* Initialize netfilter hook */
  nfho->hook      = (nf_hookfn*)hfunc;       /* hook function */
  nfho->hooknum   = NF_INET_PRE_ROUTING;     /* received packets */
  nfho->pf        = PF_INET;                 /* IPv4 */
  nfho->priority  = NF_IP_PRI_FIRST;         /* max hook priority */

  nf_register_net_hook(&init_net, nfho);
}

static void __exit LKM_exit(void)
{
  nf_unregister_net_hook(&init_net, nfho);
  kfree(nfho);
}

module_init(LKM_init);
module_exit(LKM_exit);

Makefile

obj-m += netfilter-LKM.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

在Bash Shell环境下编译,并且加载linux内核模块。

make
insmod netfilter-LKM.ko

通过 rmmod netfilter-LKM 移除linux内核模块。

参考

1 Netfilter

2 Linux Kernel Communication Netfilter Hooks

3 Netfilter Architecture

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表