Linux: Netfilter 简介

news2024/9/28 3:24:44

文章目录

  • 1. 前言
  • 2. Netfilter 简介
    • 2.1 Netfilter 的功能
    • 2.2 Netfilter 示例
    • 2.3 Netfilter 实现概览
      • 2.3.1 Netfilter hook 的 注册 和 注销
      • 2.3.2 Netfilter hook 的触发
        • 2.3.2.1 NF_INET_PRE_ROUTING
        • 2.3.2.2 NF_INET_LOCAL_IN
        • 2.3.2.3 NF_INET_FORWARD
        • 2.3.2.4 NF_INET_LOCAL_OUT
        • 2.3.2.5 NF_INET_POST_ROUTING
  • 3. Netfilter 的经典应用
  • 4. 参考资料

1. 前言

2. Netfilter 简介

2.1 Netfilter 的功能

Netfilter 有如下所列 3 大功能:

1. 数据包过滤(Packet filtering)
   负责根据规则对数据包进行过滤。

2. 网络地址转换(NAT,Network Address Translation)
   负责转换网络数据包的 IP 地址。
   NAT 是一个重要的协议,已经成为在IPv4地址耗尽的情况下保护全局地址空间的流行和必要工具。

3. 数据包篡改(Packet mangling)
   负责修改数据包内容(实际上,NAT 是数据包篡改的一种,它修改源或目标 IP 地址)。
   例如,可以修改 TCP SYN 数据包的最大段大小(MSS)值,以便允许在网络上传输大尺寸的数据包。

2.2 Netfilter 示例

本文给出一个 数据包过滤(Packet filtering)Netfilter 内核模块 示例,在 IPv4 协议栈的 NF_INET_LOCAL_IN hook 点,插入一个钩子函数 nf_test_in_hook(),对进入的网络包进行处理。示例 netfilter_kern_pf_test.c 代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>


#define NIPQUAD(addr) \
        ((unsigned char *)&addr)[0], \
        ((unsigned char *)&addr)[1], \
        ((unsigned char *)&addr)[2], \
        ((unsigned char *)&addr)[3]
#define NIPQUAD_FMT "%u.%u.%u.%u"

static unsigned int nf_pf_test_in_hook(unsigned int hook, struct sk_buff *skb, 
				const struct net_device *in, const struct net_device *out, 
				int (*okfn)(struct sk_buff*))
{
	struct ethhdr *eth_header;
	struct iphdr *ip_header;

	eth_header = (struct ethhdr *)(skb_mac_header(skb));
	ip_header = (struct iphdr *)(skb_network_header(skb));
	pr_info("dest MAC: %pM, source MAC: %pM, protocol: %x\n",
		eth_header->h_dest, eth_header->h_source, eth_header->h_proto);
	pr_info("src IP:'"NIPQUAD_FMT"', dst IP:'"NIPQUAD_FMT"' \n",
		NIPQUAD(ip_header->saddr), NIPQUAD(ip_header->daddr));

	return NF_ACCEPT;
}

static struct nf_hook_ops nf_pf_test_ops[] __read_mostly = {
	{
		.hook = (void *)nf_pf_test_in_hook,
		.pf = NFPROTO_IPV4,
		.hooknum = NF_INET_LOCAL_IN,
		.priority = NF_IP_PRI_FIRST,
	},
};

static int __init netfilter_pf_test_init(void)
{
	int ret;
	
	ret = nf_register_net_hooks(&init_net, nf_pf_test_ops, ARRAY_SIZE(nf_pf_test_ops));
	if (ret < 0) {
		pr_err("register nf packet-filter hook fail\n");
		return ret;
	}
	
	pr_info("register packet-filter test hook\n");
	
	return 0;
}

static void __exit netfilter_pf_test_exit(void)
{
	pr_info("unregister nf packet-filter test hook\n");
	
	nf_unregister_net_hooks(&init_net, nf_pf_test_ops, ARRAY_SIZE(nf_pf_test_ops));
}

module_init(netfilter_pf_test_init);
module_exit(netfilter_pf_test_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Netfliter packet-filter test");

编译 Makefile 如下:

ifneq ($(KERNELRELEASE),)

obj-m := netfilter_kern_pf_test.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions .cache.mk modules.order Module.symvers

Ubuntu 16.04 系统下编译、安装、运行:

$ make
$ sudo insmod netfilter_kern_pf_test.ko
$ dmesg

在这里插入图片描述

$ sudo rmmod netfilter_kern_pf_test
[ 4606.344200] unregister nf packet-filter test hook

2.3 Netfilter 实现概览

Netfilter 功能,首先内核要开启 CONFIG_NETFILTER 配置。在内核代码 NF_HOOK 关键字进行搜索,就可以找到所有相关的 hook 点。本文只以 IPv4 数据收、发流程中的 5 个 Netfilter hook 点做简单分析。

2.3.1 Netfilter hook 的 注册 和 注销

内核系统如下接口来 注册 和 注销 Netfilter hook 接口:

/* include/linux/netfilter.h */

/* Function to register/unregister hook points. */
int nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops);
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *ops);
int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
			unsigned int n);
void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
			unsigned int n);
static struct nf_hook_entries __rcu **nf_hook_entry_head(struct net *net, const struct nf_hook_ops *reg)
{
	if (reg->pf != NFPROTO_NETDEV)
		return net->nf.hooks[reg->pf]+reg->hooknum;
	...
}

static struct nf_hook_entries *
nf_hook_entries_grow(const struct nf_hook_entries *old,
		const struct nf_hook_ops *reg)
{
	...
	new = allocate_hook_entries_size(alloc_entries);
 	...
 	if (!inserted) {
		new_ops[nhooks] = (void *)reg;
		new->hooks[nhooks].hook = reg->hook; // Netlink hook 回调
		new->hooks[nhooks].priv = reg->priv;
	}

	return new;
}

/* 注册 netfilter hook @reg 到 网络命名空间 @net */
int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg)
{
        struct nf_hook_entries *p, *new_hooks;
        struct nf_hook_entries __rcu **pp;

	...

	/*
	 * 定位到 网络命名空间 @net 中 协议 / hook 类型
	 * (@reg->pf, @reg->hook) 对应的 Netfilter hook slot
	 */
	pp = nf_hook_entry_head(net, reg);
        if (!pp)
                return -EINVAL;

	mutex_lock(&nf_hook_mutex);

	p = nf_entry_dereference(*pp);
        new_hooks = nf_hook_entries_grow(p, reg); /* 新建一个 Netfilter hook 对象 */

	/*
	 * 将新建的 Netfilter hook 对象放置到 
	 * 网络命名空间 @net 对应的 Netfilter hook slot
	 */
	if (!IS_ERR(new_hooks))
                rcu_assign_pointer(*pp, new_hooks);

	mutex_unlock(&nf_hook_mutex);
	...

	synchronize_net();
        BUG_ON(p == new_hooks);
        kvfree(p);
        return 0;
}

2.3.2 Netfilter hook 的触发

先看一张图:
在这里插入图片描述
图中粗体字的 PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING 分别对应数据收、发过程的以下 5 个 Netfilter hook 点:

/* include/uapi/linux/netfilter.h */
enum nf_inet_hooks {
	NF_INET_PRE_ROUTING, // PREROUTING
	NF_INET_LOCAL_IN, // INPUT
	NF_INET_FORWARD, // FORWARD
	NF_INET_LOCAL_OUT, // OUTPUT
	NF_INET_POST_ROUTING, // POSTROUTING
	NF_INET_NUMHOOKS
};
2.3.2.1 NF_INET_PRE_ROUTING
/* net/ipv4/ip_input.c */

/*
 *  Main IP Receive routine.
 */
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
	...
	
	return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
		net, NULL, skb, dev, NULL,
		ip_rcv_finish);
}
/* include/linux/netfilter.h */

static inline int
NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk, struct sk_buff *skb,
	struct net_device *in, struct net_device *out,
	int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
	int ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn);
	if (ret == 1)
		ret = okfn(net, sk, skb);
	return ret;
}

/**
 * nf_hook - call a netfilter hook
 *
 * Returns 1 if the hook has allowed the packet to pass.  The function
 * okfn must be invoked by the caller in this case.  Any other return
 * value indicates the packet has been consumed by the hook.
 */
static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
     struct sock *sk, struct sk_buff *skb,
     struct net_device *indev, struct net_device *outdev,
     int (*okfn)(struct net *, struct sock *, struct sk_buff *))
{
	struct nf_hook_entries *hook_head;
	int ret = 1;
 
 	...
 	rcu_read_lock();
	hook_head = rcu_dereference(net->nf.hooks[pf][hook]); // 网络命名空间 @net 的 Netfilter hook 表
	if (hook_head) {
		struct nf_hook_state state;

		nf_hook_state_init(&state, hook, pf, indev, outdev,
				sk, net, okfn);

		ret = nf_hook_slow(skb, &state, hook_head, 0);
	}
	rcu_read_unlock();

	return ret;
}
/* net/netfilter/core.c */

/* Returns 1 if okfn() needs to be executed by the caller,
 * -EPERM for NF_DROP, 0 otherwise.  Caller must hold rcu_read_lock. */
int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state,
	const struct nf_hook_entries *e, unsigned int s)
{
	unsigned int verdict;
	int ret;

	for (; s < e->num_hook_entries; s++) {
		// 调用 nf_register_net_hooks() 系列接口注册的 hook, 如前面
		// 示例代码中的 nf_pf_test_in_hook() 接口。
		verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state);
		switch (verdict & NF_VERDICT_MASK) {
		case NF_ACCEPT: // 接受包
			break;
		case NF_DROP: // 丢弃包
			kfree_skb(skb);
			ret = NF_DROP_GETERR(verdict);
			if (ret == 0)
				ret = -EPERM;
			return ret;
		case NF_QUEUE:
			ret = nf_queue(skb, state, e, s, verdict);
			if (ret == 1)
				continue;
			return ret;
		default:
			/* Implicit handling for NF_STOLEN, as well as any other
			 * non conventional verdicts.
			 */
			return 0;
		}
	}
}
/* include/linux/netfilter.h */

static inline int
nf_hook_entry_hookfn(const struct nf_hook_entry *entry, struct sk_buff *skb,
       struct nf_hook_state *state)
{
	return entry->hook(entry->priv, skb, state);
}
2.3.2.2 NF_INET_LOCAL_IN
/* net/ipv4/ip_input.c */

/*
 *      Deliver IP Packets to the higher protocol layers.
 */
int ip_local_deliver(struct sk_buff *skb)
{
        /*
         *      Reassemble IP fragments.
         */
        struct net *net = dev_net(skb->dev);

	...

	return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,
                       net, NULL, skb, skb->dev, NULL,
                       ip_local_deliver_finish);
}

NF_HOOK()2.3.1 已经分析过,在此不再赘述。

2.3.2.3 NF_INET_FORWARD
/* net/ipv4/ip_forward.c */

int ip_forward(struct sk_buff *skb)
{
	...

	return NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
                       net, NULL, skb, skb->dev, rt->dst.dev,
                       ip_forward_finish);
	
	...
}

NF_HOOK()2.3.1 已经分析过,在此不再赘述。

2.3.2.4 NF_INET_LOCAL_OUT
/* net/ipv4/ip_output.c */

int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	...

	return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
                       net, sk, skb, NULL, skb_dst(skb)->dev,
                       dst_output);
}

NF_HOOK()2.3.1 已经分析过,在此不再赘述。

2.3.2.5 NF_INET_POST_ROUTING
/* net/ipv4/ip_output.c */

int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	...

	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
                            net, sk, skb, NULL, skb->dev,
                            ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
}

int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
	...

	return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
                            net, sk, skb, NULL, dev,
                            ip_finish_output,
                            !(IPCB(skb)->flags & IPSKB_REROUTED));
}

3. Netfilter 的经典应用

Netfilter 的经典应用示范是 iptables,IPv4协议下,iptables 通过 setsockopt() 接口和内核模块 net/ipv4/netfilter/ip_tables.c 进行交互,感兴趣的读者可自行阅读相关源码。
看两个简单的 iptables 操作的示例:

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

# 拒绝 ping 任何主机:将所有外发的 ICMP 包丢弃
iptables -A OUTPUT -p icmp -j DROP

4. 参考资料

[1] Netfilter’s flowtable infrastructure
[2] Nftables - Packet flow and Netfilter hooks in detail
[3] A Deep Dive into Iptables and Netfilter Architecture
[4] [译] 深入理解 iptables 和 netfilter 架构
[5] 一图带你看懂 Iptables 底层架构 Netfilter
[6] 理解 Linux 下的 Netfilter/iptables
[7] 走进Linux内核之Netfilter框架
[8] 从零开始基于Netfilter编写一个Linux防火墙

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1643992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

八、Linux进程检测与控制

章节目标 了解进程和程序的关系了解进程的特点能够使用top动态查看进程信息能够使用ps静态查看进程信息能够使用kill命令给进程发送信号能够调整进程的优先级&#xff08;扩展&#xff09; 引言 在运维的日常工作中&#xff0c;监视系统的运行状况是每天例行的工作&#xff…

python数据分析——业务数据描述

业务数据描述 前言一、数据收集数据信息来源 二、公司内部数据&#xff08;1&#xff09;客户资料数据&#xff08;2&#xff09;销售明细数据&#xff08;3&#xff09;营销活动数据 三、市场调查数据1 观察法2 提问法3 实验法 四、公共数据五、第三方数据六、数据预处理七、数…

Vue工程化开发和脚手架Vue CLI

目录 一、介绍 二、使用步骤 1. 全局安装&#xff08;一次&#xff09; 2.查看Vue版本 3.创建项目架子&#xff08;项目名不能使用中文&#xff09; 4.启动项目 一、介绍 Vue CLI是Vue官方提供的一个全局命令工具。可以帮助我们快速创建一个开发的Vue项目的标准化基础架子…

【HCIP】OSPF综合实验报告

一、分析要求 R4为ISP&#xff0c;直连设备间使用公网网段R3、R5、R6、R7为MGRE环境&#xff0c;R3为NHS合理划分172.16.0.0/16网段做NAT配置使私网能够访问R4环回ospf优化&#xff08;汇总和特殊区域&#xff09;&#xff0c;减少计时器时间加快收敛&#xff0c;设备之间做认…

解决:mybatisplus分页查询失效,总是查询到所有数据

目录 问题描述&#xff1a;解决方案&#xff1a;1.配置mybatisplus拦截器PaginationInterceptor2.自行编写SQL语句查询 问题描述&#xff1a; 在前端使用Map<String, Object> params&#xff0c;把page&#xff0c;limit参数作为请求体传送到后端&#xff0c;但是分页查…

WWW‘24 | 课程学习CL+模仿学习IL用于ETF及商品期货交易

WWW24 | 课程学习CL模仿学习IL用于ETF及商品期货交易 原创 QuantML QuantML 2024-05-04 13:47 论文地址&#xff1a;[2311.13326] Curriculum Learning and Imitation Learning for Model-free Control on Financial Time-series (arxiv.org) 本文探讨了在金融时间序列数据上…

vulnhub靶场之FunBox-3

一.环境搭建 1.靶场描述 Boot2Root ! Easy going, but with this Funbox you have to spend a bit more time. Much more, if you stuck in good traps. But most of the traps have hints, that they are traps. If you need hints, call me on twitter: 0815R2d2 Have fun.…

C++实验五 : 类的继承 -----CUST

【题目】 1.定义person类&#xff0c;包括数据私有成员&#xff1a;姓名&#xff0c;性别&#xff1b;共用成员函数&#xff1a;带参数构造函数&#xff0c;display函数输出本类对象的所有数据成员值。 2.定义student类&#xff0c;保护继承person类&#xff1b;增加保护数据成…

从零开始:Django项目的创建与配置指南

title: 从零开始&#xff1a;Django项目的创建与配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 后端开发 tags: DjangoWebDevPythonORMSecurityDeploymentOptimization Django简介&#xff1a; Django是一个开源的高级Python Web框架&#xff…

【Python项目】基于时间序列的【大气污染预测系统】

技术简介&#xff1a;使用Python技术、B/S架构、MYSQL数据库等实现。 系统简介&#xff1a;本系统的主要使用角色为普通用户和管理员用户&#xff0c;两者的功能几乎是一致的&#xff0c;但管理员用户比普通用户多了用户管理的功能&#xff0c;可以对系统内的用户进行管理。普通…

Vue的项目启动指令分析

通过Vue CLI脚手架创建的项目&#xff0c;默认的启动项目方式是 npm run serve 这里的serve是可以修改的。 在创建的项目目录中&#xff0c;找到package.json 双击打开&#xff0c;找到scripts部分 在scripts部分&#xff0c;有一个"serve"键值对&#xff0c;这里的…

如何进行Go语言的性能测试和调优?

文章目录 开篇一、性能测试1. 使用标准库中的testing包2. 使用第三方工具 二、性能调优1. 优化算法和数据结构2. 减少不必要的内存分配和垃圾回收3. 并发和并行 结尾 开篇 Go语言以其出色的性能和简洁的语法受到了广大开发者的喜爱。然而&#xff0c;在实际开发中&#xff0c;…

DML操作表的数据

一、增加数据 语法&#xff1a; INSERT [INTO] 表名 [( 列名表 )] VALUES ( 值列表 ) 1.1 插入全部字段 l 所有的字段名都写出来 INSERT INTO 表名 (字段名1, 字段名2, 字段名3…) VALUES (值1, 值2, 值3); l 不写字段名 INSERT INTO 表名 VALUES (值1, 值2, 值3…); 注&…

我这次没有蹭Oracle发布热度的原因

这次没有去蹭热度&#xff0c;原因有几个。 主观 确实是生病了&#xff0c;身体不舒服&#xff0c;那几个卷王在卷公众号的时候&#xff0c;我在床上卷成一团。 不和这几个打了鸡血的人比了。我卷了一点和他们不一样的。我节日期间看到我初中同班同学发的微博。 对这个就是我…

创造价值与回报:创业者的思维格局与商业智慧

在纷繁复杂的商业世界中&#xff0c;有一种信念始终贯穿于无数创业者的心中——那就是创造价值。张磊的这句“只要不断地创造价值&#xff0c;迟早会有回报”道出了创业者的核心思维格局和商业智慧。本文将从创业者的角度&#xff0c;探讨创造价值的重要性&#xff0c;以及如何…

力扣每日一题112:路径总和

题目 简单 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶子节点 是…

《原则》生活和工作 - 三余书屋 3ysw.net

原则&#xff1a;生活和工作 您好&#xff0c;今天我们解读的书是《原则&#xff1a;生活和工作》。这本书和我们之前解读过的《原则&#xff1a;应对变化中的世界秩序》是同一个作者写的。那本书的主题非常宏大&#xff0c;它讨论的是世界运行的原则。而今天我们聊的《原则&a…

消费品行业的5G新时代:2024年消费品行业趋势洞察报告

来源&#xff1a;安永 2023 年&#xff0c;我国社会消费品零售总额达人民币 47.147.147.147.1万亿元&#xff0c;比上年增长 7.2%7.2%7.2%7.2%&#xff1b;最终 消费支出拉动经济增长 4.34.34.3个百分点&#xff0c;比上年提高 3.13.13.1个百分点&#xff0c;对经济增长的贡献…

linux C语言Makefile

ChatGPT 在Linux中使用Makefile来自动化C语言项目的构建过程是很普遍的实践。Makefile是一个包含了一系列构建目标及如何构建这些目标的依赖和规则的文本文件。 一个基本的Makefile例子可能会像这样&#xff1a; # 定义编译器 CCgcc# 定义编译选项 CFLAGS-I.# 定义可执行文件…

C++ : list类及其模拟实现

目录 一、list的介绍和使用 list的介绍 list的使用 1.list的构造 构造函数 2.list iterator 的使用 3.list capacity 4.list element access 5.list modifiers 6.list的迭代器失效 二、list的模拟实现 要点 list类模拟实现部分接口全部代码展示 一、list的介绍和使…