当前位置: 首页 > news >正文

从零实现一个网络防火墙:包过滤与状态检测

前言

你有没有想过:公司网络是怎么挡住黑客攻击的?路由器上的防火墙是怎么决定哪些数据包能过、哪些不能过?

网络防火墙是网络安全的第一道防线。

今天我们用C语言从零实现一个网络防火墙的核心功能:

· 包过滤(规则匹配)
· 状态检测(连接跟踪)
· NAT(网络地址转换)
· 日志记录与告警

---

一、防火墙核心原理

1. 防火墙架构

```
┌─────────────────────────────────────────────────────────────┐
│ 网络防火墙 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 包过滤 │ │ 状态检测 │ │ NAT │ │
│ │ 规则引擎 │ │ 连接跟踪 │ │ 地址转换 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌───────────┐
│ 内网 │ │ 外网 │ │ DMZ │
│ 192.168.1.0│ │ Internet │ │ 10.0.0.0│
└───────────┘ └───────────┘ └───────────┘
```

2. 核心概念

概念 说明
包过滤 根据五元组(协议、源IP、源端口、目标IP、目标端口)决定放行/丢弃
状态检测 跟踪TCP/UDP连接状态,只允许已建立连接的相关包通过
NAT 将内网私有IP转换为公网IP
规则链 INPUT/OUTPUT/FORWARD

---

二、完整代码实现

1. 基础数据结构

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <net/if.h>

#define MAX_RULES 1000
#define MAX_CONNECTIONS 10000
#define MAX_LOG_ENTRIES 10000

// 协议类型
typedef enum {
PROTO_TCP = 6,
PROTO_UDP = 17,
PROTO_ICMP = 1,
PROTO_ANY = 0
} protocol_t;

// 动作
typedef enum {
ACTION_ALLOW = 0,
ACTION_DENY = 1,
ACTION_REJECT = 2,
ACTION_LOG = 3,
ACTION_NAT = 4
} action_t;

// 方向
typedef enum {
DIR_IN = 0,
DIR_OUT = 1,
DIR_FORWARD = 2
} direction_t;

// 防火墙规则
typedef struct fw_rule {
int id;
direction_t direction;
protocol_t protocol;
struct in_addr src_ip;
struct in_addr src_mask;
uint16_t src_port_min;
uint16_t src_port_max;
struct in_addr dst_ip;
struct in_addr dst_mask;
uint16_t dst_port_min;
uint16_t dst_port_max;
action_t action;
int log;
int enabled;
struct fw_rule *next;
} fw_rule_t;

// TCP连接状态
typedef enum {
TCP_STATE_NEW = 0,
TCP_STATE_ESTABLISHED,
TCP_STATE_CLOSING,
TCP_STATE_TIMEWAIT
} tcp_state_t;

// 连接跟踪条目
typedef struct conn_track {
struct in_addr src_ip;
uint16_t src_port;
struct in_addr dst_ip;
uint16_t dst_port;
protocol_t protocol;
tcp_state_t state;
time_t create_time;
time_t last_activity;
int packet_count;
int bytes_transferred;
uint8_t tcp_flags;
struct conn_track *next;
} conn_track_t;

// NAT映射
typedef struct nat_mapping {
struct in_addr internal_ip;
uint16_t internal_port;
struct in_addr external_ip;
uint16_t external_port;
protocol_t protocol;
time_t create_time;
time_t expire_time;
struct nat_mapping *next;
} nat_mapping_t;

// 日志条目
typedef struct log_entry {
time_t timestamp;
struct in_addr src_ip;
uint16_t src_port;
struct in_addr dst_ip;
uint16_t dst_port;
protocol_t protocol;
int action_taken;
char message[256];
struct log_entry *next;
} log_entry_t;

// 防火墙
typedef struct firewall {
fw_rule_t *rules;
conn_track_t *connections;
nat_mapping_t *nat_mappings;
log_entry_t *logs;
int rule_count;
int conn_count;
int log_count;
pthread_mutex_t mutex;
int running;
pthread_t monitor_thread;
} firewall_t;
```

2. 规则引擎

```c
// 创建防火墙
firewall_t *fw_create() {
firewall_t *fw = malloc(sizeof(firewall_t));
memset(fw, 0, sizeof(firewall_t));
fw->running = 1;
pthread_mutex_init(&fw->mutex, NULL);
printf("[防火墙] 初始化完成\n");
return fw;
}

// 添加规则
void fw_add_rule(firewall_t *fw, int id, direction_t dir, protocol_t proto,
const char *src_ip, const char *src_mask, uint16_t src_port_min,
uint16_t src_port_max, const char *dst_ip, const char *dst_mask,
uint16_t dst_port_min, uint16_t dst_port_max, action_t action,
int log) {
pthread_mutex_lock(&fw->mutex);

fw_rule_t *rule = malloc(sizeof(fw_rule_t));
rule->id = id;
rule->direction = dir;
rule->protocol = proto;
inet_pton(AF_INET, src_ip, &rule->src_ip);
inet_pton(AF_INET, src_mask, &rule->src_mask);
rule->src_port_min = src_port_min;
rule->src_port_max = src_port_max;
inet_pton(AF_INET, dst_ip, &rule->dst_ip);
inet_pton(AF_INET, dst_mask, &rule->dst_mask);
rule->dst_port_min = dst_port_min;
rule->dst_port_max = dst_port_max;
rule->action = action;
rule->log = log;
rule->enabled = 1;
rule->next = fw->rules;
fw->rules = rule;
fw->rule_count++;

pthread_mutex_unlock(&fw->mutex);
printf("[规则] 添加规则 #%d: %s\n", id,
action == ACTION_ALLOW ? "ALLOW" :
action == ACTION_DENY ? "DENY" : "REJECT");
}

// IP匹配
int ip_match(struct in_addr ip, struct in_addr pattern, struct in_addr mask) {
uint32_t ip_net = ip.s_addr & mask.s_addr;
uint32_t pattern_net = pattern.s_addr & mask.s_addr;
return ip_net == pattern_net;
}

// 端口匹配
int port_match(uint16_t port, uint16_t min, uint16_t max) {
return port >= min && port <= max;
}

// 规则匹配
fw_rule_t *fw_match_rule(firewall_t *fw, direction_t dir, protocol_t proto,
struct in_addr src_ip, uint16_t src_port,
struct in_addr dst_ip, uint16_t dst_port) {
pthread_mutex_lock(&fw->mutex);

fw_rule_t *rule = fw->rules;
while (rule) {
if (rule->enabled && rule->direction == dir) {
// 协议匹配
if (rule->protocol != PROTO_ANY && rule->protocol != proto) {
rule = rule->next;
continue;
}

// IP匹配
if (!ip_match(src_ip, rule->src_ip, rule->src_mask) ||
!ip_match(dst_ip, rule->dst_ip, rule->dst_mask)) {
rule = rule->next;
continue;
}

// 端口匹配(TCP/UDP)
if (proto == PROTO_TCP || proto == PROTO_UDP) {
if (!port_match(src_port, rule->src_port_min, rule->src_port_max) ||
!port_match(dst_port, rule->dst_port_min, rule->dst_port_max)) {
rule = rule->next;
continue;
}
}

pthread_mutex_unlock(&fw->mutex);
return rule;
}
rule = rule->next;
}

pthread_mutex_unlock(&fw->mutex);
return NULL;
}
```

3. 状态检测(连接跟踪)

```c
// 查找或创建连接跟踪
conn_track_t *fw_get_connection(firewall_t *fw, struct in_addr src_ip, uint16_t src_port,
struct in_addr dst_ip, uint16_t dst_port,
protocol_t proto, int create) {
pthread_mutex_lock(&fw->mutex);

conn_track_t *conn = fw->connections;
while (conn) {
if (conn->src_ip.s_addr == src_ip.s_addr &&
conn->src_port == src_port &&
conn->dst_ip.s_addr == dst_ip.s_addr &&
conn->dst_port == dst_port &&
conn->protocol == proto) {
// 更新活动时间
conn->last_activity = time(NULL);
conn->packet_count++;
pthread_mutex_unlock(&fw->mutex);
return conn;
}
conn = conn->next;
}

if (!create) {
pthread_mutex_unlock(&fw->mutex);
return NULL;
}

// 创建新连接
conn = malloc(sizeof(conn_track_t));
conn->src_ip = src_ip;
conn->src_port = src_port;
conn->dst_ip = dst_ip;
conn->dst_port = dst_port;
conn->protocol = proto;
conn->state = TCP_STATE_NEW;
conn->create_time = time(NULL);
conn->last_activity = time(NULL);
conn->packet_count = 1;
conn->bytes_transferred = 0;
conn->tcp_flags = 0;
conn->next = fw->connections;
fw->connections = conn;
fw->conn_count++;

pthread_mutex_unlock(&fw->mutex);
return conn;
}

// 更新连接状态
void fw_update_conn_state(conn_track_t *conn, uint8_t tcp_flags) {
if (conn->protocol != PROTO_TCP) return;

// 简化TCP状态机
if (tcp_flags & 0x02) { // SYN
if (conn->state == TCP_STATE_NEW) {
conn->state = TCP_STATE_ESTABLISHED;
}
} else if (tcp_flags & 0x01) { // FIN
conn->state = TCP_STATE_CLOSING;
} else if (tcp_flags & 0x04) { // RST
conn->state = TCP_STATE_CLOSING;
}

conn->tcp_flags = tcp_flags;
}

// 清理过期连接
void fw_cleanup_connections(firewall_t *fw) {
time_t now = time(NULL);

pthread_mutex_lock(&fw->mutex);

conn_track_t *conn = fw->connections;
conn_track_t *prev = NULL;

while (conn) {
// TCP连接超过60秒无活动,清理
// UDP连接超过30秒无活动,清理
int timeout = (conn->protocol == PROTO_TCP) ? 60 : 30;
if (now - conn->last_activity > timeout) {
conn_track_t *to_free = conn;
if (prev) {
prev->next = conn->next;
} else {
fw->connections = conn->next;
}
conn = conn->next;
free(to_free);
fw->conn_count--;
} else {
prev = conn;
conn = conn->next;
}
}

pthread_mutex_unlock(&fw->mutex);
}
```

4. NAT实现

```c
// 创建NAT映射
nat_mapping_t *fw_create_nat(firewall_t *fw, struct in_addr internal_ip,
uint16_t internal_port, struct in_addr external_ip,
uint16_t external_port, protocol_t proto) {
pthread_mutex_lock(&fw->mutex);

nat_mapping_t *nat = malloc(sizeof(nat_mapping_t));
nat->internal_ip = internal_ip;
nat->internal_port = internal_port;
nat->external_ip = external_ip;
nat->external_port = external_port;
nat->protocol = proto;
nat->create_time = time(NULL);
nat->expire_time = time(NULL) + 300; // 5分钟超时
nat->next = fw->nat_mappings;
fw->nat_mappings = nat;

pthread_mutex_unlock(&fw->mutex);
return nat;
}

// 查找NAT映射
nat_mapping_t *fw_find_nat(firewall_t *fw, struct in_addr ip, uint16_t port,
protocol_t proto, int internal) {
pthread_mutex_lock(&fw->mutex);

nat_mapping_t *nat = fw->nat_mappings;
while (nat) {
if (internal) {
if (nat->internal_ip.s_addr == ip.s_addr &&
nat->internal_port == port &&
nat->protocol == proto) {
pthread_mutex_unlock(&fw->mutex);
return nat;
}
} else {
if (nat->external_ip.s_addr == ip.s_addr &&
nat->external_port == port &&
nat->protocol == proto) {
pthread_mutex_unlock(&fw->mutex);
return nat;
}
}
nat = nat->next;
}

pthread_mutex_unlock(&fw->mutex);
return NULL;
}
```

5. 包处理

```c
// 模拟包处理
int fw_process_packet(firewall_t *fw, direction_t dir, protocol_t proto,
struct in_addr src_ip, uint16_t src_port,
struct in_addr dst_ip, uint16_t dst_port,
uint8_t tcp_flags, int length) {
char src_str[32], dst_str[32];
inet_ntop(AF_INET, &src_ip, src_str, sizeof(src_str));
inet_ntop(AF_INET, &dst_ip, dst_str, sizeof(dst_str));

printf("[包] %s → %s:%d proto=%d\n", src_str, dst_str, dst_port, proto);

// 1. 规则匹配
fw_rule_t *rule = fw_match_rule(fw, dir, proto, src_ip, src_port, dst_ip, dst_port);

// 2. 状态检测
conn_track_t *conn = NULL;
if (proto == PROTO_TCP || proto == PROTO_UDP) {
conn = fw_get_connection(fw, src_ip, src_port, dst_ip, dst_port, proto, 1);
if (conn) {
fw_update_conn_state(conn, tcp_flags);
}
}

// 3. 应用规则
if (rule) {
if (rule->action == ACTION_ALLOW) {
// 如果是NAT,进行地址转换
if (rule->action == ACTION_NAT) {
// 简化NAT
printf("[NAT] 转换地址\n");
}
printf("[允许] 包通过\n");
return 1;
} else if (rule->action == ACTION_DENY) {
printf("[拒绝] 包被丢弃\n");
return 0;
} else if (rule->action == ACTION_REJECT) {
printf("[拒绝] 包被拒绝(发送ICMP不可达)\n");
return 0;
}
}

// 默认策略:DROP
printf("[默认] 包被丢弃\n");
return 0;
}
```

6. 监控与日志

```c
// 记录日志
void fw_log(firewall_t *fw, struct in_addr src_ip, uint16_t src_port,
struct in_addr dst_ip, uint16_t dst_port, protocol_t proto,
int action, const char *message) {
pthread_mutex_lock(&fw->mutex);

if (fw->log_count >= MAX_LOG_ENTRIES) {
// 删除最旧的日志
log_entry_t *entry = fw->logs;
fw->logs = entry->next;
free(entry);
fw->log_count--;
}

log_entry_t *log = malloc(sizeof(log_entry_t));
log->timestamp = time(NULL);
log->src_ip = src_ip;
log->src_port = src_port;
log->dst_ip = dst_ip;
log->dst_port = dst_port;
log->protocol = proto;
log->action_taken = action;
strcpy(log->message, message);
log->next = fw->logs;
fw->logs = log;
fw->log_count++;

pthread_mutex_unlock(&fw->mutex);
}

// 监控线程(清理连接、检查规则)
void *fw_monitor_thread(void *arg) {
firewall_t *fw = (firewall_t*)arg;

while (fw->running) {
sleep(10);
fw_cleanup_connections(fw);
}

return NULL;
}
```

7. 测试代码

```c
void test_firewall() {
printf("=== 网络防火墙测试 ===\n\n");

firewall_t *fw = fw_create();

// 添加规则
fw_add_rule(fw, 1, DIR_IN, PROTO_TCP,
"0.0.0.0", "0.0.0.0", 0, 0,
"192.168.1.10", "255.255.255.255", 80, 80,
ACTION_ALLOW, 1);

fw_add_rule(fw, 2, DIR_IN, PROTO_TCP,
"0.0.0.0", "0.0.0.0", 0, 0,
"192.168.1.10", "255.255.255.255", 22, 22,
ACTION_DENY, 1);

fw_add_rule(fw, 3, DIR_IN, PROTO_ANY,
"192.168.1.0", "255.255.255.0", 0, 0,
"0.0.0.0", "0.0.0.0", 0, 0,
ACTION_ALLOW, 0);

// 启动监控线程
pthread_t monitor_tid;
pthread_create(&monitor_tid, NULL, fw_monitor_thread, fw);

// 模拟网络包
struct in_addr src, dst;
inet_pton(AF_INET, "192.168.1.100", &src);
inet_pton(AF_INET, "192.168.1.10", &dst);

// HTTP请求(应该允许)
fw_process_packet(fw, DIR_IN, PROTO_TCP, src, 54321, dst, 80, 0x02, 1500);

// SSH请求(应该拒绝)
fw_process_packet(fw, DIR_IN, PROTO_TCP, src, 54322, dst, 22, 0x02, 100);

// 内网到外网(应该允许)
struct in_addr src_in, dst_out;
inet_pton(AF_INET, "192.168.1.100", &src_in);
inet_pton(AF_INET, "8.8.8.8", &dst_out);
fw_process_packet(fw, DIR_OUT, PROTO_UDP, src_in, 12345, dst_out, 53, 0, 512);

// 外网到内网(默认拒绝)
struct in_addr src_ext, dst_int;
inet_pton(AF_INET, "1.2.3.4", &src_ext);
inet_pton(AF_INET, "192.168.1.100", &dst_int);
fw_process_packet(fw, DIR_IN, PROTO_TCP, src_ext, 80, dst_int, 12345, 0, 0);

sleep(2);
fw->running = 0;
pthread_join(monitor_tid, NULL);

printf("\n防火墙统计:\n");
printf(" 规则数: %d\n", fw->rule_count);
printf(" 连接数: %d\n", fw->conn_count);
printf(" 日志数: %d\n", fw->log_count);

free(fw);
}

int main() {
test_firewall();
return 0;
}
```

---

三、编译和运行

```bash
gcc -o firewall firewall.c -lpthread
./firewall
```

---

四、iptables vs 本实现

特性 本实现 iptables
包过滤 ✅ ✅
状态检测 ✅ ✅
NAT ✅ ✅
规则链 INPUT/OUTPUT/FORWARD INPUT/OUTPUT/FORWARD/PREROUTING/POSTROUTING
性能 演示级 生产级
配置 编程配置 命令行

---

五、总结

通过这篇文章,你学会了:

· 防火墙的核心功能(包过滤、状态检测、NAT)
· 规则匹配引擎(五元组匹配)
· 连接跟踪(TCP状态机)
· NAT地址转换
· 日志与监控

网络防火墙是网络安全的基石。掌握它,你就理解了iptables、pfSense的底层设计。

下一篇预告:《从零实现一个入侵检测系统:Snort的核心设计》

---

评论区分享一下你对防火墙的理解~

http://www.jsqmd.com/news/1132030/

相关文章:

  • DeepSeek-Translator v2 API 实战:3步调用实现《大学英语》课文批量英译中
  • 2026年想找专业靠谱的外墙保温装饰一体板供应商 看这几点就够了
  • 《Python + Streamlit + DeepSeek API 实现一个本地文档问答助手》
  • STFT 与 DWT 实战对比:Python 3.11 下 5 种窗函数对非平稳信号时频分析效果
  • SQL Server 自定义函数进阶:WITH SCHEMABINDING 与参数默认值实战解析
  • 达朗贝尔公式与特征线法:一维波动方程依赖区间与决定区域图解
  • MySQL 8.0 自定义函数实战:3种类型对比与5个业务场景代码实例
  • Dify低代码AI开发平台:从零部署到工作流实战全指南
  • 我为什么放弃Scrapy转投Playwright?爬虫框架选择的真相
  • CUDA 12.4 + cuDNN 8.9 环境配置:Windows/Linux 双系统 5 步验证法
  • Codex Windows Sandbox 启动失败:CreateProcessAsUserW failed: 2 的原因与修复
  • MatAnyone:无需绿幕的AI视频抠像神器,轻松实现专业级视频背景分离
  • Win11Debloat:Windows系统清理优化的终极免费解决方案
  • MySQL 8.0 CTE vs 子查询:5个复杂场景下的性能与可读性对比
  • 本地AI绘图新范式:Codex与Cowart插件实现指哪改哪交互式创作
  • 《数据库系统概论》第6版 vs 第5版:3大核心内容更新与SQL Server/Oracle 23版适配
  • ssm267防疫信息登记系统的设计与实现+jsp(文档+源码)_kaic
  • 终极免费显存检测工具:5分钟找出显卡隐藏故障
  • WinForms 3类Timer深度对比:UI线程、线程池与服务器计时器选型指南
  • 和也磁疗床垫实测分享,聊聊网传磁疗有效吗相关疑问
  • 5分钟快速掌握AKShare:零基础上手金融数据接口库的终极指南
  • GESP2026年6月认证C++一级( 第一部分选择题(1-7))精讲
  • Visual C++ AIO运行时库:Windows系统必备的终极解决方案
  • VGGish vs Wav2Vec 2.0:2种音频特征提取方案在3个下游任务上的性能对比
  • StatefulSet vs Deployment 深度对比:5个关键差异与3个典型选型场景
  • 效率直接起飞!盘点2026年巅峰之作的AI论文写作工具
  • LLM评测与可观测工具对比分析
  • GPT-4o 与 Claude 3.5 翻译对比:评测8篇《大学英语》课文的3个关键维度
  • bert-ancient-chinese 模型部署与实战:Hugging Face 3行代码调用,EvaHan 2022 任务F1提升0.3%
  • SQL Server vs MySQL 函数开发:从5个关键差异到跨平台迁移指南