找回密码
 立即注册
首页 业界区 安全 使用netfilter_queue重定向IP数据包

使用netfilter_queue重定向IP数据包

杭环 昨天 16:30
使用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开发工具包
  1. sudo apt-get install libnetfilter-queue-dev
复制代码
 三、实现

主要步骤如下:

  • 设置Iptables规则,将发送到本机2001端口的TCP数据包引导至用户态程序处理。
  • 编写用户态程序,使用libnetfilter_queue 库接收数据包,修改目标地址和端口,然后放行。
  • 处理数据包,在程序中修改IP头和TCP头,并重新计算校验和。
1、配置Iptables规则

 首先,需要设置iptables规则,将目标端口为2001的tcp数据包送入NFQUEUE。假设使用队列编号为0:
  1. # 在 mangle 表的 OUTPUT 链添加规则(针对本机发出的包)
  2. iptables -t mangle -A OUTPUT -p tcp --dport 2001 -j NFQUEUE --queue-num 0
  3. # 在 mangle 表的 PREROUTING 链添加规则(针对转发的包)
  4. iptables -t mangle -A PREROUTING -p tcp --dport 2001 -j NFQUEUE --queue-num 0
复制代码
避免循环转发,如果转发数据包再次匹配iptables规则,会导致循环。可以考虑使用数据包的mark字段来标记已处理的数据包,避免再次进入队列。
  1. # 标记已处理的数据包,避免重复进入队列
  2. iptables -t mangle -A PREROUTING -p tcp --dport 2001 -j MARK --set-mark 1
  3. iptables -t mangle -A PREROUTING -p tcp --dport 2001 -m mark ! --mark 1 -j NFQUEUE
复制代码
注意:
测试完毕之后,记得用下列指令删除规则:
  1. iptables -t mangle -D
复制代码
2、编写用户态程序

 
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <netinet/in.h>
  5. #include <netinet/ip.h>
  6. #include <netinet/tcp.h>
  7. #include
  8. #include <linux/netfilter.h>
  9. #include <libnetfilter_queue/libnetfilter_queue.h>
  10. // 计算校验和的辅助函数
  11. static uint16_t checksum(uint32_t init, const uint8_t *data, size_t len) {
  12.     uint32_t sum = init;
  13.     while (len > 1) {
  14.         sum += *((uint16_t *)data);
  15.         data += 2;
  16.         len -= 2;
  17.     }
  18.     if (len > 0) {
  19.         sum += *((uint8_t *)data);
  20.     }
  21.     while (sum >> 16) {
  22.         sum = (sum & 0xFFFF) + (sum >> 16);
  23.     }
  24.     return (uint16_t)~sum;
  25. }
  26. // 计算TCP校验和(需要伪头)
  27. static uint16_t tcp_checksum(struct iphdr *iph, struct tcphdr *tcph, size_t tcp_len) {
  28.     uint32_t sum = 0;
  29.     struct pseudo_header {
  30.         uint32_t src_addr;
  31.         uint32_t dest_addr;
  32.         uint8_t zero;
  33.         uint8_t protocol;
  34.         uint16_t tcp_len;
  35.     } psh;
  36.     psh.src_addr = iph->saddr;
  37.     psh.dest_addr = iph->daddr;
  38.     psh.zero = 0;
  39.     psh.protocol = IPPROTO_TCP;
  40.     psh.tcp_len = htons(tcp_len);
  41.     sum = checksum(0, (uint8_t *)&psh, sizeof(psh));
  42.     return checksum(sum, (uint8_t *)tcph, tcp_len);
  43. }
  44. // NFQUEUE 回调函数
  45. static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa, void *data) {
  46.     (void)nfmsg; (void)data;
  47.     uint32_t id = 0;
  48.     unsigned char *packet_data;
  49.     int packet_len;
  50.     struct nfqnl_msg_packet_hdr *ph;
  51.     ph = nfq_get_msg_packet_hdr(nfa);
  52.     if (ph) {
  53.         id = ntohl(ph->packet_id);
  54.     }
  55.     // 获取整个数据包(包括IP头)
  56.     packet_len = nfq_get_payload(nfa, (char **)&packet_data);
  57.     if (packet_len < 0) {
  58.         printf("Failed to get payload\n");
  59.         return NF_ACCEPT;
  60.     }
  61.     // 解析IP头和TCP头
  62.     struct iphdr *iph = (struct iphdr *)packet_data;
  63.     if (iph->protocol != IPPROTO_TCP) { // 只处理TCP
  64.         return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
  65.     }
  66.     size_t ip_hdr_len = iph->ihl * 4;
  67.     struct tcphdr *tcph = (struct tcphdr *)(packet_data + ip_hdr_len);
  68.     size_t tcp_len = ntohs(iph->tot_len) - ip_hdr_len;
  69.     // 修改目标IP地址和端口
  70.     struct in_addr new_dest_addr;
  71.     inet_pton(AF_INET, "192.168.0.2", &new_dest_addr);
  72.     iph->daddr = new_dest_addr.s_addr; // 修改IP头中的目标IP
  73.     tcph->dest = htons(9000); // 修改TCP头中的目标端口
  74.     // 重新计算IP头和TCP头的校验和
  75.     iph->check = 0;
  76.     iph->check = htons(checksum(0, (uint8_t *)iph, ip_hdr_len)); // 注意网络字节序
  77.     tcph->check = 0;
  78.     tcph->check = htons(tcp_checksum(iph, tcph, tcp_len)); // 注意网络字节序
  79.     // 放行修改后的数据包
  80.     return nfq_set_verdict(qh, id, NF_ACCEPT, packet_len, packet_data);
  81. }
  82. int main() {
  83.     struct nfq_handle *h;
  84.     struct nfq_q_handle *qh;
  85.     int fd, rv;
  86.     char buf[4096];
  87.     h = nfq_open();
  88.     if (!h) {
  89.         fprintf(stderr, "Error during nfq_open()\n");
  90.         exit(1);
  91.     }
  92.     if (nfq_unbind_pf(h, AF_INET) < 0) {
  93.         fprintf(stderr, "Error during nfq_unbind_pf()\n");
  94.         exit(1);
  95.     }
  96.     if (nfq_bind_pf(h, AF_INET) < 0) {
  97.         fprintf(stderr, "Error during nfq_bind_pf()\n");
  98.         exit(1);
  99.     }
  100.     qh = nfq_create_queue(h, 0, &cb, NULL); // 使用队列0
  101.     if (!qh) {
  102.         fprintf(stderr, "Error during nfq_create_queue()\n");
  103.         exit(1);
  104.     }
  105.     if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) { // 拷贝整个数据包
  106.         fprintf(stderr, "Can't set packet copy mode\n");
  107.         exit(1);
  108.     }
  109.     fd = nfq_fd(h);
  110.     while ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
  111.         nfq_handle_packet(h, buf, rv);
  112.     }
  113.     nfq_destroy_queue(qh);
  114.     nfq_close(h);
  115.     return 0;
  116. }
复制代码
编译:
  1. gcc -o nfq_forward nfq_forward.c -lnetfilter_queue
复制代码
运行:
  1. sudo ./nfq_forward
复制代码
 

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册