使用netfilter_queue重定向IP数据包(单向)
一、需求
使用libnetfilter_queue实现在192.168.0.92服务器2001端口收到的数据包转发至192.168.0.54服务器9000端口。
二、开发环境
OS:Debian 12
gcc :15.1.0
IP:192.168.0.92
安装libnetfilter-queue-dev开发工具包- sudo apt-get install libnetfilter-queue-dev
复制代码 三、实现
主要步骤如下:
- 设置Iptables规则,将发送到本机2001端口的TCP数据包引导至用户态程序处理。
- 编写用户态程序,使用libnetfilter_queue 库接收数据包,修改目标地址和端口,然后放行。
- 处理数据包,在程序中修改IP头和TCP头,并重新计算校验和。
1、配置Iptables规则
首先,需要设置iptables规则,将目标端口为2001的tcp数据包送入NFQUEUE。假设使用队列编号为0:- # 在 mangle 表的 OUTPUT 链添加规则(针对本机发出的包)
- iptables -t mangle -A OUTPUT -p tcp --dport 2001 -j NFQUEUE --queue-num 0
- # 在 mangle 表的 PREROUTING 链添加规则(针对转发的包)
- iptables -t mangle -A PREROUTING -p tcp --dport 2001 -j NFQUEUE --queue-num 0
复制代码 避免循环转发,如果转发数据包再次匹配iptables规则,会导致循环。可以考虑使用数据包的mark字段来标记已处理的数据包,避免再次进入队列。- # 标记已处理的数据包,避免重复进入队列
- iptables -t mangle -A PREROUTING -p tcp --dport 2001 -j MARK --set-mark 1
- iptables -t mangle -A PREROUTING -p tcp --dport 2001 -m mark ! --mark 1 -j NFQUEUE
复制代码 注意:
测试完毕之后,记得用下列指令删除规则:2、编写用户态程序
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <netinet/tcp.h>
- #include
- #include <linux/netfilter.h>
- #include <libnetfilter_queue/libnetfilter_queue.h>
- // 计算校验和的辅助函数
- static uint16_t checksum(uint32_t init, const uint8_t *data, size_t len) {
- uint32_t sum = init;
- while (len > 1) {
- sum += *((uint16_t *)data);
- data += 2;
- len -= 2;
- }
- if (len > 0) {
- sum += *((uint8_t *)data);
- }
- while (sum >> 16) {
- sum = (sum & 0xFFFF) + (sum >> 16);
- }
- return (uint16_t)~sum;
- }
- // 计算TCP校验和(需要伪头)
- static uint16_t tcp_checksum(struct iphdr *iph, struct tcphdr *tcph, size_t tcp_len) {
- uint32_t sum = 0;
- struct pseudo_header {
- uint32_t src_addr;
- uint32_t dest_addr;
- uint8_t zero;
- uint8_t protocol;
- uint16_t tcp_len;
- } psh;
- psh.src_addr = iph->saddr;
- psh.dest_addr = iph->daddr;
- psh.zero = 0;
- psh.protocol = IPPROTO_TCP;
- psh.tcp_len = htons(tcp_len);
- sum = checksum(0, (uint8_t *)&psh, sizeof(psh));
- return checksum(sum, (uint8_t *)tcph, tcp_len);
- }
- // NFQUEUE 回调函数
- static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) {
- (void)nfmsg; (void)data;
- uint32_t id = 0;
- unsigned char *packet_data;
- int packet_len;
- struct nfqnl_msg_packet_hdr *ph;
- ph = nfq_get_msg_packet_hdr(nfa);
- if (ph) {
- id = ntohl(ph->packet_id);
- }
- // 获取整个数据包(包括IP头)
- packet_len = nfq_get_payload(nfa, (char **)&packet_data);
- if (packet_len < 0) {
- printf("Failed to get payload\n");
- return NF_ACCEPT;
- }
- // 解析IP头和TCP头
- struct iphdr *iph = (struct iphdr *)packet_data;
- if (iph->protocol != IPPROTO_TCP) { // 只处理TCP
- return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
- }
- size_t ip_hdr_len = iph->ihl * 4;
- struct tcphdr *tcph = (struct tcphdr *)(packet_data + ip_hdr_len);
- size_t tcp_len = ntohs(iph->tot_len) - ip_hdr_len;
- // 修改目标IP地址和端口
- struct in_addr new_dest_addr;
- inet_pton(AF_INET, "192.168.0.2", &new_dest_addr);
- iph->daddr = new_dest_addr.s_addr; // 修改IP头中的目标IP
- tcph->dest = htons(9000); // 修改TCP头中的目标端口
- // 重新计算IP头和TCP头的校验和
- iph->check = 0;
- iph->check = htons(checksum(0, (uint8_t *)iph, ip_hdr_len)); // 注意网络字节序
- tcph->check = 0;
- tcph->check = htons(tcp_checksum(iph, tcph, tcp_len)); // 注意网络字节序
- // 放行修改后的数据包
- return nfq_set_verdict(qh, id, NF_ACCEPT, packet_len, packet_data);
- }
- int main() {
- struct nfq_handle *h;
- struct nfq_q_handle *qh;
- int fd, rv;
- char buf[4096];
- h = nfq_open();
- if (!h) {
- fprintf(stderr, "Error during nfq_open()\n");
- exit(1);
- }
- if (nfq_unbind_pf(h, AF_INET) < 0) {
- fprintf(stderr, "Error during nfq_unbind_pf()\n");
- exit(1);
- }
- if (nfq_bind_pf(h, AF_INET) < 0) {
- fprintf(stderr, "Error during nfq_bind_pf()\n");
- exit(1);
- }
- qh = nfq_create_queue(h, 0, &cb, NULL); // 使用队列0
- if (!qh) {
- fprintf(stderr, "Error during nfq_create_queue()\n");
- exit(1);
- }
- if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) { // 拷贝整个数据包
- fprintf(stderr, "Can't set packet copy mode\n");
- exit(1);
- }
- fd = nfq_fd(h);
- while ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
- nfq_handle_packet(h, buf, rv);
- }
- nfq_destroy_queue(qh);
- nfq_close(h);
- return 0;
- }
复制代码 编译:- gcc -o nfq_forward nfq_forward.c -lnetfilter_queue
复制代码 运行:
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |