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

📅 2026/7/6 2:37:53
从零实现一个网络防火墙:包过滤与状态检测
前言你有没有想过公司网络是怎么挡住黑客攻击的路由器上的防火墙是怎么决定哪些数据包能过、哪些不能过网络防火墙是网络安全的第一道防线。今天我们用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/UDPif (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) { // SYNif (conn-state TCP_STATE_NEW) {conn-state TCP_STATE_ESTABLISHED;}} else if (tcp_flags 0x01) { // FINconn-state TCP_STATE_CLOSING;} else if (tcp_flags 0x04) { // RSTconn-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) {// 简化NATprintf([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;}}// 默认策略DROPprintf([默认] 包被丢弃\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. 测试代码cvoid 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;}---三、编译和运行bashgcc -o firewall firewall.c -lpthread./firewall---四、iptables vs 本实现特性 本实现 iptables包过滤 ✅ ✅状态检测 ✅ ✅NAT ✅ ✅规则链 INPUT/OUTPUT/FORWARD INPUT/OUTPUT/FORWARD/PREROUTING/POSTROUTING性能 演示级 生产级配置 编程配置 命令行---五、总结通过这篇文章你学会了· 防火墙的核心功能包过滤、状态检测、NAT· 规则匹配引擎五元组匹配· 连接跟踪TCP状态机· NAT地址转换· 日志与监控网络防火墙是网络安全的基石。掌握它你就理解了iptables、pfSense的底层设计。下一篇预告《从零实现一个入侵检测系统Snort的核心设计》---评论区分享一下你对防火墙的理解