Netfilter hook 状态连接数据包过滤
Netfilter hook stateful connection packet filtering
我正在编写一个 Netfilter 挂钩,并希望对传入的 TCP 数据包进行状态分析,无论它们属于现有连接还是新连接正在启动。
这是我第一次尝试使用 Netfilter 编写代码,在阅读 https://people.netfilter.org/pablo/docs/login.pdf 之后我明白我需要检查数据包是否被归类为 NEW 或 ESTABLISHED 状态。但是我找不到任何关于如何为此编写代码的文档。
static unsigned int hfunc(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct iphdr *iph;
struct udphdr *udph;
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_TCP) {
/*
if packet SYN flag is enabled and state==NEW:
return NF_ACCEPT
else if SYN flag is disabled and state==NEW:
return NF_DROP
*/
}
return NF_ACCEPT
}
static int __init my_net_module_init(void) {
printk(KERN_INFO "Initializing my netfilter module\n");
// Allocating memory for hook structure.
my_nf_hook = (struct nf_hook_ops*) kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL);
// Constructing the structure
my_nf_hook->hook = (nf_hookfn*)hfunc; /* hook function */
my_nf_hook->hooknum = NF_INET_PRE_ROUTING; /* received packets */
my_nf_hook->pf = PF_INET; /* IPv4 */
my_nf_hook->priority = NF_IP_PRI_FIRST; /* max hook priority */
nf_register_net_hook(&init_net, my_nf_hook);
return 0;
}
static void __exit my_net_module_exit(void) {
nf_unregister_net_hook(&init_net, my_nf_hook);
kfree(my_nf_hook);
printk(KERN_INFO "Exiting my netfilter module\n");
}
module_init(my_net_module_init);
module_exit(my_net_module_exit);
编辑:
添加了用于在预路由中注册钩子的代码片段。
似乎在你的钩子中你想根据关于连接状态的 conntrack(CT) 信息对数据包做出决定 - 阻止(丢弃)连接中间的所有 TCP 数据包,即数据包既在 CT 中没有 SYN 标志和连接条目。
所以想要收获CT的好处,就得让他多干点活儿。
现在你的挂钩在 NF_INET_PRE_ROUTING
中,优先级为 NF_IP_PRI_FIRST
。只需查看 Linux 内核数据包流的 picture。如果我们谈论预路由链 CT 处理在某个地方 after RAW table(即具有较低的优先级)。
可以看到的优先级列表here:
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
因此要在 CT 之后(在 nf_conntrack_in()
之后)坚持下去,您必须注册优先级低于 NF_IP_PRI_CONNTRACK
的钩子(即具有更大的数字,例如 -50
)。
所以你这样做:
static struct nf_hook_ops hooks[] __read_mostly = {
{
.hook = hfunc,
.pf = PF_INET,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_CONNTRACK + 150
},
// ...
};
// ...
int ret;
ret = nf_register_hooks(hooks, ARRAY_SIZE(hooks));
if (ret < 0)
// error
那么您应该从您的挂钩中访问 CT 信息:
static unsigned int hfunc(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state) {
struct iphdr *iph;
iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_TCP) {
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
struct tcphdr *tcph;
ct = nf_ct_get(skb, &ctinfo);
if (!ct)
return NF_ACCEPT;
tcph = tcp_hdr(skb)
if (tcph->syn) { // && !tcph->ack ???
if (ctinfo == IP_CT_NEW)
return NF_ACCEPT;
} else {
if (ctinfo == IP_CT_NEW)
return NF_DROP;
}
}
return NF_ACCEPT
}
还要记住CT必须参与你的Linux内核网络处理。应该有 CT 模块插入内核并添加适当的 iptables 规则。
我正在编写一个 Netfilter 挂钩,并希望对传入的 TCP 数据包进行状态分析,无论它们属于现有连接还是新连接正在启动。 这是我第一次尝试使用 Netfilter 编写代码,在阅读 https://people.netfilter.org/pablo/docs/login.pdf 之后我明白我需要检查数据包是否被归类为 NEW 或 ESTABLISHED 状态。但是我找不到任何关于如何为此编写代码的文档。
static unsigned int hfunc(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct iphdr *iph;
struct udphdr *udph;
if (!skb)
return NF_ACCEPT;
iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_TCP) {
/*
if packet SYN flag is enabled and state==NEW:
return NF_ACCEPT
else if SYN flag is disabled and state==NEW:
return NF_DROP
*/
}
return NF_ACCEPT
}
static int __init my_net_module_init(void) {
printk(KERN_INFO "Initializing my netfilter module\n");
// Allocating memory for hook structure.
my_nf_hook = (struct nf_hook_ops*) kzalloc(sizeof(struct nf_hook_ops), GFP_KERNEL);
// Constructing the structure
my_nf_hook->hook = (nf_hookfn*)hfunc; /* hook function */
my_nf_hook->hooknum = NF_INET_PRE_ROUTING; /* received packets */
my_nf_hook->pf = PF_INET; /* IPv4 */
my_nf_hook->priority = NF_IP_PRI_FIRST; /* max hook priority */
nf_register_net_hook(&init_net, my_nf_hook);
return 0;
}
static void __exit my_net_module_exit(void) {
nf_unregister_net_hook(&init_net, my_nf_hook);
kfree(my_nf_hook);
printk(KERN_INFO "Exiting my netfilter module\n");
}
module_init(my_net_module_init);
module_exit(my_net_module_exit);
编辑: 添加了用于在预路由中注册钩子的代码片段。
似乎在你的钩子中你想根据关于连接状态的 conntrack(CT) 信息对数据包做出决定 - 阻止(丢弃)连接中间的所有 TCP 数据包,即数据包既在 CT 中没有 SYN 标志和连接条目。
所以想要收获CT的好处,就得让他多干点活儿。
现在你的挂钩在 NF_INET_PRE_ROUTING
中,优先级为 NF_IP_PRI_FIRST
。只需查看 Linux 内核数据包流的 picture。如果我们谈论预路由链 CT 处理在某个地方 after RAW table(即具有较低的优先级)。
可以看到的优先级列表here:
enum nf_ip_hook_priorities {
NF_IP_PRI_FIRST = INT_MIN,
NF_IP_PRI_CONNTRACK_DEFRAG = -400,
NF_IP_PRI_RAW = -300,
NF_IP_PRI_SELINUX_FIRST = -225,
NF_IP_PRI_CONNTRACK = -200,
NF_IP_PRI_MANGLE = -150,
NF_IP_PRI_NAT_DST = -100,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_SECURITY = 50,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = 300,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};
因此要在 CT 之后(在 nf_conntrack_in()
之后)坚持下去,您必须注册优先级低于 NF_IP_PRI_CONNTRACK
的钩子(即具有更大的数字,例如 -50
)。
所以你这样做:
static struct nf_hook_ops hooks[] __read_mostly = {
{
.hook = hfunc,
.pf = PF_INET,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_CONNTRACK + 150
},
// ...
};
// ...
int ret;
ret = nf_register_hooks(hooks, ARRAY_SIZE(hooks));
if (ret < 0)
// error
那么您应该从您的挂钩中访问 CT 信息:
static unsigned int hfunc(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state) {
struct iphdr *iph;
iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_TCP) {
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
struct tcphdr *tcph;
ct = nf_ct_get(skb, &ctinfo);
if (!ct)
return NF_ACCEPT;
tcph = tcp_hdr(skb)
if (tcph->syn) { // && !tcph->ack ???
if (ctinfo == IP_CT_NEW)
return NF_ACCEPT;
} else {
if (ctinfo == IP_CT_NEW)
return NF_DROP;
}
}
return NF_ACCEPT
}
还要记住CT必须参与你的Linux内核网络处理。应该有 CT 模块插入内核并添加适当的 iptables 规则。