深入理解Netfilter
Netfilter 介绍
Netfilter是Linux内核中的一个框架,用于进行网络数据包的过滤和操作。它提供了强大的网络数据包处理功能,使系统管理员能够在Linux系统上实施高级的网络安全策略,包括防火墙、网络地址转换(NAT)、流量控制等。
Netfilter的核心是一个包过滤器,它可以在数据包通过网络协议栈的不同阶段进行处理。当数据包经过网络协议栈时,Netfilter可以检查、修改或丢弃这些数据包,从而允许管理员根据自己的需求对网络流量进行控制和管理。
Netfilter的主要组件包括:
-
Hooks(钩子):Netfilter在网络协议栈中的不同阶段插入了一系列钩子,以便进行数据包处理。这些钩子包括PREROUTING、INPUT、FORWARD、OUTPUT和POSTROUTING等,分别对应着数据包进入和离开网络栈的不同阶段。
-
Tables(表):Netfilter使用表来组织和存储规则集合。每个表都由多个规则组成,用于定义对数据包的处理方式。常见的表包括filter表、nat表和mangle表等。
-
Chains(链):每个表中都包含多个链,用于将规则按顺序连接起来,形成数据包处理的流程。常见的链包括INPUT、OUTPUT和FORWARD等。
-
Matches(匹配器):Netfilter使用匹配器来检查数据包是否符合规则的条件。匹配器可以根据源地址、目标地址、协议类型、端口号等进行数据包的匹配。
-
Targets(目标):当数据包匹配了规则的条件后,Netfilter将根据规则中指定的目标进行相应的操作。常见的目标包括ACCEPT(接受数据包)、DROP(丢弃数据包)、REJECT(拒绝数据包)等。
通过使用Netfilter,管理员可以根据自己的需求配置防火墙规则、进行网络地址转换、实施流量控制等操作。它为Linux系统提供了强大的网络安全和管理功能。
Hooks使用
在Netfilter中,Hooks(钩子)是用于在数据包通过网络协议栈的不同阶段进行处理的组件。下面是几个示例,展示了如何使用Hooks进行特定操作:
PREROUTING Hook:PREROUTING钩子位于数据包进入网络协议栈的早期阶段,可以用于实施网络地址转换(NAT)等操作。以下是一个PREROUTING钩子的示例,将来自外部网络的数据包的目标IP地址进行转换:
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// PREROUTING钩子的处理函数
static unsigned int prerouting_hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 在此处进行目标IP地址的转换操作
return NF_ACCEPT; // 允许数据包继续传递
}
// 注册PREROUTING钩子
static struct nf_hook_ops prerouting_hook_ops = {
.hook = prerouting_hook_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册PREROUTING钩子
int __init my_module_init(void)
{
nf_register_hook(&prerouting_hook_ops);
return 0;
}
// 在模块退出时注销PREROUTING钩子
void __exit my_module_exit(void)
{
nf_unregister_hook(&prerouting_hook_ops);
}
module_init(my_module_init);
module_exit(my_module_exit);
FORWARD Hook:FORWARD钩子位于数据包在网络协议栈中进行转发时的阶段,可以用于实施流量控制等操作。以下是一个FORWARD钩子的示例,阻止从特定源IP地址发出的数据包进行转发:
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// FORWARD钩子的处理函数
static unsigned int forward_hook_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 检查源IP地址,如果满足特定条件,则丢弃数据包
if (ip_header->saddr == htonl(0x12345678))
return NF_DROP; // 丢弃数据包
return NF_ACCEPT; // 允许数据包继续传递
}
// 注册FORWARD钩子
static struct nf_hook_ops forward_hook_ops = {
.hook = forward_hook_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册FORWARD钩子
int __init my_module_init(void)
{
nf_register_hook(&forward_hook_ops);
return 0;
}
Tables(表)使用
在Netfilter中,Tables(表)是用于组织和管理规则的组件。下面是几个示例,展示了如何使用Tables来管理规则:
Filter表:Filter表用于过滤数据包,可以根据规则允许或拒绝数据包通过。以下是一个使用Filter表的示例,允许源IP地址为特定值的数据包通过:
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// Filter表的处理函数
static unsigned int filter_table_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 检查源IP地址,如果满足特定条件,则允许数据包通过
if (ip_header->saddr == htonl(0x12345678))
return NF_ACCEPT; // 允许数据包通过
return NF_DROP; // 拒绝数据包
}
// 注册Filter表的钩子
static struct nf_hook_ops filter_table_ops = {
.hook = filter_table_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册Filter表的钩子
int __init my_module_init(void)
{
nf_register_hook(&filter_table_ops);
return 0;
}
// 在模块退出时注销Filter表的钩子
void __exit my_module_exit(void)
{
nf_unregister_hook(&filter_table_ops);
}
module_init(my_module_init);
module_exit(my_module_exit);
NAT表:NAT表用于实施网络地址转换,允许将数据包的源IP地址或目标IP地址进行转换。以下是一个使用NAT表的示例,将数据包的源IP地址进行转换:
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// NAT表的处理函数
static unsigned int nat_table_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 在此处进行源IP地址转换操作
return NF_ACCEPT; // 允许数据包继续传递
}
// 注册NAT表的钩子
static struct nf_hook_ops nat_table_ops = {
.hook = nat_table_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册NAT表的钩子
int __init my_module_init(void)
{
nf_register_hook(&nat_table_ops);
return 0;
}
// 在模块退出时注销NAT表的钩子
void __exit my_module_exit(void)
{
nf_unregister_hook(&nat_table_ops);
}
module_init(my_module_init);
module_exit(my_module_exit);
这些示例展示了在Linux内核中如何使用Tables(表)组件来管理规则。在实际应用中,可以根据需要使用不同的Tables(表)来管理规则。
使用其他类型的表:
Mangle表
Mangle表用于修改数据包的头部信息,例如修改TTL(Time to Live)值或设置标记等。以下是一个使用Mangle表的示例,将数据包的TTL值减少1
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// Mangle表的处理函数
static unsigned int mangle_table_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 修改TTL值
ip_header->ttl--;
return NF_ACCEPT; // 允许数据包继续传递
}
// 注册Mangle表的钩子
static struct nf_hook_ops mangle_table_ops = {
.hook = mangle_table_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册Mangle表的钩子
int __init my_module_init(void)
{
nf_register_hook(&mangle_table_ops);
return 0;
}
// 在模块退出时注销Mangle表的钩子
void __exit my_module_exit(void)
{
nf_unregister_hook(&mangle_table_ops);
}
module_init(my_module_init);
module_exit(my_module_exit);
Raw表
Raw表用于处理特殊的数据包,例如ICMP(Internet Control Message Protocol)错误报文。以下是一个使用Raw表的示例,对ICMP错误报文进行处理:
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/icmp.h>
// Raw表的处理函数
static unsigned int raw_table_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 检查数据包是否为ICMP错误报文
if (ip_header->protocol == IPPROTO_ICMP) {
struct icmphdr *icmp_header = icmp_hdr(skb);
// 在此处处理ICMP错误报文
// ...
}
return NF_ACCEPT; // 允许数据包继续传递
}
// 注册Raw表的钩子
static struct nf_hook_ops raw_table_ops = {
.hook = raw_table_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册Raw表的钩子
int __init my_module_init(void)
{
nf_register_hook(&raw_table_ops);
return 0;
}
// 在模块退出时注销Raw表的钩子
void __exit my_module_exit(void)
{
nf_unregister_hook(&raw_table_ops);
}
module_init(my_module_init);
module_exit(my_module_exit);
Chains(链)组件
Netfilter中的Chains(链)组件用于对数据包进行处理和过滤。Chains可以被认为是一系列规则的集合,它们按照一定的顺序依次应用于数据包。每个表都包含多个预定义的Chains,例如在Filter表中有INPUT、FORWARD和OUTPUT等Chains。
以下是一些示例,展示了如何使用Chains组件:
INPUT链
Filter表中的INPUT链:这个示例展示了如何使用Filter表中的INPUT链来过滤接收到的数据包,只允许特定源IP的数据包通过,其他数据包则被丢弃。
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// Filter表中INPUT链的处理函数
static unsigned int input_chain_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 只允许源IP为192.168.0.1的数据包通过
if (ip_header->saddr == htonl(0xC0A80001)) {
return NF_ACCEPT; // 允许数据包通过
} else {
return NF_DROP; // 丢弃数据包
}
}
// 注册Filter表中INPUT链的钩子
static struct nf_hook_ops input_chain_ops = {
.hook = input_chain_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册Filter表中INPUT链的钩子
int __init my_module_init(void)
{
nf_register_hook(&input_chain_ops);
return 0;
}
// 在模块退出时注销Filter表中INPUT链的钩子
void __exit my_module_exit(void)
{
nf_unregister_hook(&input_chain_ops);
}
module_init(my_module_init);
module_exit(my_module_exit);
FORWARD链
Filter表中的FORWARD链:这个示例展示了如何使用Filter表中的FORWARD链来修改目标IP,并将数据包转发到指定的目标IP。
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// Filter表中FORWARD链的处理函数
static unsigned int forward_chain_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 修改目标IP为192.168.0.100
ip_header->daddr = htonl(0xC0A80064);
return NF_ACCEPT; // 允许数据包继续转发
}
// 注册Filter表中FORWARD链的钩子
static struct nf_hook_ops forward_chain_ops = {
.hook = forward_chain_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST,
};
// 在模块初始化时注册Filter表中FORWARD链的钩子
int __init my_module_init(void)
{
nf_register_hook(&forward_chain_ops);
return 0;
}
// 在模块退出时注销Filter表中FORWARD链的钩子
void __exit my_module_exit(void)
{
nf_unregister_hook(&forward_chain_ops);
}
module_init(my_module_init);
module_exit(my_module_exit);
这些示例展示了如何使用Chains组件进行数据包处理和过滤。根据需要,可以根据具体的场景和要求来定义和注册适当的处理函数和钩子,以实现所需的功能。
Matches(匹配器)组件
Netfilter中的Matches(匹配器)组件用于根据特定的条件对数据包进行匹配。匹配器可以用于过滤规则的条件判断,以决定是否对数据包进行处理或丢弃。
以下是一些示例,展示了如何使用Matches组件:
源IP匹配器
这个示例展示了如何使用源IP匹配器来匹配特定的源IP地址,并对匹配的数据包进行处理。
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// 源IP匹配器的处理函数
static int source_ip_match_func(const struct sk_buff *skb, const struct xt_match_param *par)
{
struct iphdr *ip_header = ip_hdr(skb);
__be32 source_ip = ip_header->saddr;
// 匹配源IP为192.168.0.1的数据包
if (source_ip == htonl(0xC0A80001)) {
return true; // 匹配成功
} else {
return false; // 匹配失败
}
}
// 源IP匹配器的数据结构
static struct xt_match source_ip_match = {
.name = "source_ip",
.revision = 0,
.family = NFPROTO_IPV4, // 仅适用于IPv4协议
.match = source_ip_match_func,
.matchsize = sizeof(struct xt_match),
.me = THIS_MODULE,
};
// 在模块初始化时注册源IP匹配器
int __init my_module_init(void)
{
xt_register_match(&source_ip_match);
return 0;
}
// 在模块退出时注销源IP匹配器
void __exit my_module_exit(void)
{
xt_unregister_match(&source_ip_match);
}
module_init(my_module_init);
module_exit(my_module_exit);
目标端口匹配器
这个示例展示了如何使用目标端口匹配器来匹配特定的目标端口,并对匹配的数据包进行处理。
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
// 目标端口匹配器的处理函数
static int dest_port_match_func(const struct sk_buff *skb, const struct xt_match_param *par)
{
struct tcphdr *tcp_header = tcp_hdr(skb);
__be16 dest_port = tcp_header->dest;
// 匹配目标端口为80的数据包
if (dest_port == htons(80)) {
return true; // 匹配成功
} else {
return false; // 匹配失败
}
}
// 目标端口匹配器的数据结构
static struct xt_match dest_port_match = {
.name = "dest_port",
.revision = 0,
.family = NFPROTO_IPV4, // 仅适用于IPv4协议
.match = dest_port_match_func,
.matchsize = sizeof(struct xt_match),
.me = THIS_MODULE,
};
// 在模块初始化时注册目标端口匹配器
int __init my_module_init(void)
{
xt_register_match(&dest_port_match);
return 0;
}
// 在模块退出时注销目标端口匹配器
void __exit my_module_exit(void)
{
xt_unregister_match(&dest_port_match);
}
module_init(my_module_init);
module_exit(my_module_exit);
这些示例展示了如何使用Matches组件来创建自定义的匹配器,并将其注册到Netfilter框架中。根据具体的需求,可以定义和实现适当的匹配函数,并根据条件返回true或false来指示匹配结果。
Targets(目标)组件
Netfilter中的Targets(目标)组件用于对匹配成功的数据包进行特定的操作或修改。目标可以用于修改数据包的头部信息、丢弃数据包、重定向数据包等。
以下是一些示例,展示了如何使用Targets组件:
修改源IP地址
这个示例展示了如何使用目标组件来修改数据包的源IP地址。
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
// 修改源IP地址的目标函数
static unsigned int modify_source_ip_target_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
struct iphdr *ip_header = ip_hdr(skb);
// 修改源IP地址为192.168.0.2
ip_header->saddr = htonl(0xC0A80002);
return NF_ACCEPT; // 接受数据包并继续处理
}
// 修改源IP地址的目标结构
static struct nf_hook_ops modify_source_ip_target = {
.hook = modify_source_ip_target_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_PRE_ROUTING, // 在数据包进入路由前进行处理
.priority = NF_IP_PRI_FIRST, // 优先级设为最高
};
// 在模块初始化时注册修改源IP地址的目标
int __init my_module_init(void)
{
nf_register_hook(&modify_source_ip_target);
return 0;
}
// 在模块退出时注销修改源IP地址的目标
void __exit my_module_exit(void)
{
nf_unregister_hook(&modify_source_ip_target);
}
module_init(my_module_init);
module_exit(my_module_exit);
丢弃数据包
这个示例展示了如何使用目标组件来丢弃匹配成功的数据包。
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
// 丢弃数据包的目标函数
static unsigned int drop_packet_target_func(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
return NF_DROP; // 丢弃数据包
}
// 丢弃数据包的目标结构
static struct nf_hook_ops drop_packet_target = {
.hook = drop_packet_target_func,
.pf = NFPROTO_IPV4, // 仅适用于IPv4协议
.hooknum = NF_INET_FORWARD, // 在数据包转发时进行处理
.priority = NF_IP_PRI_FIRST, // 优先级设为最高
};
// 在模块初始化时注册丢弃数据包的目标
int __init my_module_init(void)
{
nf_register_hook(&drop_packet_target);
return 0;
}
// 在模块退出时注销丢弃数据包的目标
void __exit my_module_exit(void)
{
nf_unregister_hook(&drop_packet_target);
}
module_init(my_module_init);
module_exit(my_module_exit);
这些示例展示了如何使用Targets组件来创建自定义的目标,并将其注册到Netfilter框架中。
推荐一个零声学院免费教程,个人觉得老师讲得不错,分享给大家,有兴趣可以去看看:[Linux,Nginx,DPDK等技术内容,点击立即学习: 链接.