Linux: Netfilter 简介

文章目录

  • 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.mfbz.cn/a/594747.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】——用户和权限管理(二)

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

Day15-JavaWeb开发-Maven高级-分模块设计与开发继承与聚合私服

1. Maven高级-分模块设计与开发 2. Maven高级-继承与聚合 2.1 继承关系实现 2.2 版本锁定 2.3 聚合实现 3. Maven高级-私服 3.1 私服-介绍 3.2 私服-资源上传与下载 4. Web开发-完结

【mysql】深入探索mysql中的各种约束条件

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

快速的异地组网工具?

【天联】是一款能够快速搭建异地组网的工具&#xff0c;其应用场景非常广泛。 零售、收银软件应用&#xff1a;通过结合【天联】&#xff0c;医药、餐饮、商超等零售行业可以实现异地统一管理。不论是分布在不同地区的门店&#xff0c;还是总部和各个分支机构&#xff0c;都可以…

工业光源环形系列一平面无影光源特点

产品特点 ◆LED灯珠均匀排布经过漫射板特殊角度反射达到漫射效果&#xff1a; ◆光源均匀性高&#xff0c;漫射效果好。

浪漫编码:手把手教你实现校园表白墙功能

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;浪漫编码&#xff1a;手把手教你实现校园表白墙功能 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 这里写目录标题 表白墙数据准备引入MyBatis和MySQL驱动依赖…

PyGame 文字显示问题及解决方法

在 Pygame 中显示文字时可能会遇到一些问题&#xff0c;例如文字显示不清晰、字体不正确或者文字位置不准确等。以下是一些常见的问题及其解决方法&#xff0c;具体情况可以看看情况。 1、问题背景 一位用户在使用 PyGame 库进行游戏开发时&#xff0c;遇到了一个问题&#xf…

【Canvas】给图片绘制矩形以及添加文字

效果图: <!DOCTYPE html> <html lang"en"><head><title>Canvas Marker Example</title></head><body><!-- 图片 --><imgid"myImage"src图片地址alt"Image to mark"style"display: no…

植物生态化学计量主要理论和假说

1 功能关联假说 描述化学计量特征与植物生长功能的关联, 主要包括: (1) 生长速率假说(Growth Rate Hypothesis) (Sterner & Elser, 2002): 随生长速率增加, 植物N:P和C:P呈降低趋势, 而P 含量呈增加趋势。该假说有助于理解植物生长速率的调控机制, 但受其他因素调控…

Oracle Database 23ai 正式发布,超级巨兽(集关系型、向量、文档、图、缓存、分布式数据库一体的全能数据库)

Oracle23c改名为Oracle23ai&#xff0c;也意味着Oracle数据库正式从Cloud进入AI时代。Oracle23ai版本是一个超级巨兽&#xff0c;简单总结下&#xff1a; AI能力&#xff1a;内置向量数据库&#xff0c;内置ONNX模型数据处理&#xff0c;内置Text2SQL&#xff0c;内置的机器学习…

Keepalived实现LVS高可用

6.1 KeepalivedLVS集群介绍 Keepalived和LVS共同构建了一个高效的负载均衡和高可用性解决方案&#xff1a;LVS作为负载均衡器&#xff0c;负责在集群中的多个服务器间分配流量&#xff0c;以其高性能和可扩展性确保应用程序能够处理大量的并发请求&#xff1b;而Keepalived则作…

llama3 史上最强开源大模型,赶超GTP-4,逼宫OpenAI

2024年4月18日&#xff0c;Meta公司推出了开源大语言模型Llama系列的最新产品—Llama 3&#xff0c;包含了80亿参数的Llama 3 8B和700亿参数的Llama 3 70B两个版本。Meta称其为“迄今为止最强的开源大模型”。 怪兽级性能 LLaMA3 提供了不同参数规模的版本&#xff0c;以适应…

【ARM Cortex-M3指南】6:异常

文章目录 六、异常6.1 异常类型6.2 优先级定义6.3 向量表6.4 中断输入和挂起行为6.5 错误异常6.5.1 总线错误6.5.2 存储器管理错误6.5.3 使用错误6.5.4 硬件错误6.5.5 处理错误 6.6 请求管理调用和可挂起的服务调用 六、异常 6.1 异常类型 Cortex-M3内置的异常架构支持多个系…

vue快速入门(五十六)具名插槽

注释很详细&#xff0c;直接上代码 上一篇 新增内容 具名插槽基本用法 源码 App.vue <template><div id"app"><h1>被淡化的背景内容</h1><my-dialog><!-- 插槽内容 --><!-- 使用插槽的名字进行对应v-slot:可以简写为# 未命名…

nginx--rewrite

功能 Nginx服务器利用ngx_http_rewrite_module 模块解析和处理理rewrite请求&#xff0c;此功能依靠PCRE(Perl Compatible Regular Expressions)&#xff0c;因此编译之前要安装PCRE库&#xff0c;rewrite是nginx服务器的重要功能之一&#xff0c;用于实现URL的重写&#xff0…

微搭低代码入门04数据模型

目录 1 创建数据模型2 一对多3 通用选项集4 API总结 上一篇我们介绍了页面管理&#xff0c;页面是盛放组件的容器&#xff0c;组件在配置属性的时候需要进行数据绑定。数据是通过创建数据模型来进行存储&#xff0c;本篇我们介绍一下数据模型的相关操作。 1 创建数据模型 微搭…

AnaTraf网络流量分析仪:网络性能监测与诊断的利器

背景 在当今数字化时代&#xff0c;网络性能监测与诊断(Network Performance Monitoring and Diagnosis, NPMD)成为了企业和组织管理网络的重要一环。为了帮助企业更好地实现网络性能的监控和故障排除&#xff0c;AnaTraf的网络流量分析仪应运而生。 AnaTraf网络流量分析仪是…

数据存储-SQLite

一般使用到数据库存储&#xff0c;涉及到的数据量都较大&#xff0c;采用文件存储也能完成&#xff0c;但是文件操作复杂&#xff0c;效率低&#xff0c;大量结构化数据通常采用关系型数据库存储较为合适。Android中已经嵌入了轻量级的关系型数据库SQLite&#xff0c;直接按照数…

数据库复习2

试述SQL的特点 有两个关系 S(A,B,C, D)和 T(C,D,E,F)&#xff0c;写出与下列查询等价的 SQL 表达式: 用SQL语句建立第2章习题6中的4个表&#xff1b;针对建立的4个表用SQL完成第2章习题6中的查询 针对习题4中的4个表试用SQL完成以下各项操作 (1)找出所有供应商的姓名和所在城市…

【高阶数据结构(一)】并查集详解

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:高阶数据结构专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Go语言知识   &#x1f51d;&#x1f51d; 高阶数据结构 1. 前言2. 并查集…
最新文章