Deep understanding of Netfilter

Netfilter Introduction

Netfilter is a framework in the Linux kernel for filtering and operating network data packets. It provides powerful network packet processing capabilities, enabling system administrators to implement advanced network security policies on Linux systems, including firewalls, Network Address Translation (NAT), flow control, etc.

At its core, Netfilter is a packet filter that processes packets at different stages as they pass through the network protocol stack. Netfilter can inspect, modify, or discard data packets as they pass through the network protocol stack, allowing administrators to control and manage network traffic according to their needs.

The main components of Netfilter include:

  • Hooks: Netfilter inserts a series of hooks at different stages in the network protocol stack for packet processing. These hooks include PREROUTING, INPUT, FORWARD, OUTPUT and POSTROUTING, etc., which correspond to different stages of data packets entering and leaving the network stack.

  • Tables: Netfilter uses tables to organize and store rule sets. Each table consists of multiple rules that define how packets are handled. Common tables include filter tables, nat tables, mangle tables, etc.

  • Chains: Each table contains multiple chains, which are used to connect rules in order to form a data packet processing process. Common chains include INPUT, OUTPUT, FORWARD, etc.

  • Matches: Netfilter uses matchers to check whether packets meet the conditions of the rule. The matcher can match data packets based on source address, destination address, protocol type, port number, etc.

  • Targets: When the data packet matches the conditions of the rule, Netfilter will perform corresponding operations based on the targets specified in the rule. Common targets include ACCEPT (accept packets), DROP (drop packets), REJECT (reject packets), etc.

By using Netfilter, administrators can configure firewall rules, perform network address translation, and implement traffic control according to their own needs. It provides powerful network security and management functions for Linux systems.

Hooks use

In Netfilter, Hooks are components used to process packets at different stages through the network protocol stack. Here are a few examples showing how to use Hooks to perform specific operations:

PREROUTING Hook: The PREROUTING hook is located in the early stage of the data packet entering the network protocol stack and can be used to implement operations such as Network Address Translation (NAT). The following is an example of a PREROUTING hook that translates the destination IP address of packets coming from an external network:

#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: The FORWARD hook is located at the stage when data packets are forwarded in the network protocol stack and can be used to implement operations such as flow control. The following is an example of a FORWARD hook that prevents packets from a specific source IP address from being forwarded:

#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 (table) use

In Netfilter, Tables are components used to organize and manage rules. Here are a few examples showing how to use Tables to manage rules:

Filter table: The Filter table is used to filter data packets and can allow or deny data packets to pass according to rules. The following is an example of using a Filter table to allow packets with a specific source IP address to pass:

#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 table: The NAT table is used to implement network address translation, allowing the source IP address or destination IP address of the data packet to be translated. The following is an example of using a NAT table to translate the source IP address of a packet:

#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);

These examples show how to use the Tables component to manage rules in the Linux kernel. In practical applications, different Tables can be used to manage rules as needed.

Use other types of tables:

Mangle table

The Mangle table is used to modify the header information of the data packet, such as modifying the TTL (Time to Live) value or setting the tag. Here is an example using a Mangle table to reduce the TTL value of a packet by 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 table

The Raw table is used to process special data packets, such as ICMP (Internet Control Message Protocol) error messages. The following is an example of using a Raw table to process ICMP error messages:

#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 component

The Chains component in Netfilter is used to process and filter data packets. Chains can be thought of as a collection of rules that are applied to packets in a certain order. Each table contains multiple predefined Chains. For example, there are Chains such as INPUT, FORWARD, and OUTPUT in the Filter table.

Here are some examples showing how to use the Chains component:

INPUT chain

INPUT chain in the Filter table: This example shows how to use the INPUT chain in the Filter table to filter received packets, allowing only packets from a specific source IP to pass through, while other packets are discarded.

#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 chain

FORWARD chain in the Filter table: This example shows how to use the FORWARD chain in the Filter table to modify the target IP and forward the packet to the specified target 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);

These examples show how to use Chains components for packet processing and filtering. As needed, appropriate processing functions and hooks can be defined and registered based on specific scenarios and requirements to achieve the required functionality.

Matches component

The Matches component in Netfilter is used to match data packets based on specific conditions. Matchers can be used to conditionally judge filtering rules to decide whether to process or discard data packets.

Here are some examples showing how to use the Matches component:

Source IP matcher

This example shows how to use a source IP matcher to match a specific source IP address and process matching packets.

#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);

target port matcher

This example shows how to use a destination port matcher to match a specific destination port and process matching packets.

#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);

These examples show how to use the Matches component to create custom matchers and register them with the Netfilter framework. Depending on the specific needs, an appropriate matching function can be defined and implemented, and returns true or false to indicate the matching result based on the condition.

Targets component

The Targets component in Netfilter is used to perform specific operations or modifications on successfully matched data packets. Targets can be used to modify packet header information, drop packets, redirect packets, etc.

Here are some examples showing how to use the Targets component:

Modify source IP address

This example shows how to use the destination component to modify the source IP address of a packet.

#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);

Drop packets

This example shows how to use the target component to drop packets that match successfully.

#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);

These examples show how to use the Targets component to create custom targets and register them with the Netfilter framework.

Recommend a free tutorial from Lingsheng Academy. I personally think the teacher’s teaching is good. I would like to share it with you. If you are interested, you can check it out: [Linux, Nginx, DPDK and other technical content, click to learn now: < a i=1>Link.

Guess you like

Origin blog.csdn.net/weixin_36184908/article/details/130852301