免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4985 | 回复: 2
打印 上一主题 下一主题

用netfilter_queue 在用户态修改网络数据包的例子程序 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-07-12 05:26 |只看该作者 |倒序浏览
原文 : http://blog.chinaunix.net/u/8057/showart_431074.html

libnetfilter_queue缺乏文档,看着libnetfilter_queue自带的例子弄的。现在写不出有头有尾的文章了,就贴贴代码(还没注释),提几个我记得的几个注意点就不写了,见谅。

程序功能, 将输出端目的地为 220.181.37.55 的包,都改为目的地为 64.233.189.104,输入段反之,达到DNAT的一小半功能,完整的NAT要做状态记录的。
注意点 :

1 - 2.6.23 的内核有BUG, nfq_unbind_pf 返回值不正确,见 : http://article.gmane.org/gmane.c ... ilter.general/33573

2 - TCP 要做正确的checksum重新计算,否则包发不出去。UDP也是,不过这段程序里没写,如果你有兴趣就练练手吧。

3- iptables 的 QUEUE target 内核模块不返回 XT_CONTINUE/IPT_CONTINUE 的——NF_QUEUE是一个netfilter的机制,不是x_tables的——所以如果要在QUEUE之后继续遍历 iptables的规则就需要点小技巧,技巧在下面的loader.sh程序里展示了:通过在mangle表里的mark来判断是第一次进入QUEUE还是第二次。而且规则是放在最前的,保证不影响后面的mark和用户态的traffict control程序。


就这些了,不写了,贴代码。

Makefile :
nf_queue_test : nf_queue_test.c
    gcc $(CFLAGS) -lnetfilter_queue $< -o $@

clean :
    rm -f nf_queue_test

  nf_queue_test.c :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <asm/byteorder.h>
#include <linux/netfilter.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <linux/ip.h>
#include <linux/tcp.h>

#ifdef __LITTLE_ENDIAN
#define IPQUAD(addr) \
  ((unsigned char *)&addr)[0],                  \
    ((unsigned char *)&addr)[1],                \
    ((unsigned char *)&addr)[2],                \
    ((unsigned char *)&addr)[3]
#else
#define IPQUAD(addr)                            \
  ((unsigned char *)&addr)[3],                  \
    ((unsigned char *)&addr)[2],                \
    ((unsigned char *)&addr)[1],                \
    ((unsigned char *)&addr)[0]
#endif

static u_int16_t checksum(u_int32_t init, u_int8_t *addr, size_t count){
  /* Compute Internet Checksum for "count" bytes
   * beginning at location "addr".
   
*/
  u_int32_t sum = init;

  while( count > 1 ) {
    /* This is the inner loop */
    sum += ntohs(* (u_int16_t*) addr);
    addr += 2;
    count -= 2;
  }

  /* Add left-over byte, if any */
  if( count > 0 )
    sum += * (u_int8_t *) addr;

  /* Fold 32-bit sum to 16 bits */
  while (sum>>16)
    sum = (sum & 0xffff) + (sum >> 16);

  return (u_int16_t)~sum;
}

static u_int16_t ip_checksum(struct iphdr* iphdrp){
  return checksum(0, (u_int8_t*)iphdrp, iphdrp->ihl<<2);
}

static void set_ip_checksum(struct iphdr* iphdrp){
  iphdrp->check = 0;
  iphdrp->check = htons(checksum(0, (u_int8_t*)iphdrp, iphdrp->ihl<<2));
}

static u_int16_t tcp_checksum2(struct iphdr* iphdrp, struct tcphdr* tcphdrp){
  size_t tcplen = ntohs(iphdrp->tot_len) - (iphdrp->ihl<<2);
  u_int32_t cksum = 0;

  cksum += ntohs((iphdrp->saddr >> 16) & 0x0000ffff);
  cksum += ntohs(iphdrp->saddr & 0x0000ffff);
  cksum += ntohs((iphdrp->daddr >> 16) & 0x0000ffff);
  cksum += ntohs(iphdrp->daddr & 0x0000ffff);
  cksum += iphdrp->protocol & 0x00ff;
  cksum += tcplen;
  return checksum(cksum, (u_int8_t*)tcphdrp, tcplen);
}

static u_int16_t tcp_checksum1(struct iphdr* iphdrp){
  struct tcphdr *tcphdrp =
    (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));

  return tcp_checksum2(iphdrp, tcphdrp);
}

static void set_tcp_checksum2(struct iphdr* iphdrp, struct tcphdr* tcphdrp){
  tcphdrp->check = 0;
  tcphdrp->check = htons(tcp_checksum2(iphdrp, tcphdrp));
}

static void set_tcp_checksum1(struct iphdr* iphdrp){
  struct tcphdr *tcphdrp =
    (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));

  set_tcp_checksum2(iphdrp, tcphdrp);
}

static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
              struct nfq_data *nfa, void *data){
  (void)nfmsg;
  (void)data;
  u_int32_t id = 0;
  struct nfqnl_msg_packet_hdr *ph;
  unsigned char *pdata = NULL;
  int pdata_len;

  ph = nfq_get_msg_packet_hdr(nfa);
  if (ph){
    id = ntohl(ph->packet_id);
  }

  pdata_len = nfq_get_payload(nfa, (char**)&pdata);
  if(pdata_len == -1){
    pdata_len = 0;
  }

  struct iphdr *iphdrp = (struct iphdr *)pdata;

  printf("len %d iphdr %d %u.%u.%u.%u ->",
         pdata_len,
         iphdrp->ihl<<2,
         IPQUAD(iphdrp->saddr));
  printf(" %u.%u.%u.%u %s",
         IPQUAD(iphdrp->daddr),
         getprotobynumber(iphdrp->protocol)->p_name);
  printf(" ipsum %hu", ip_checksum(iphdrp));
  if(iphdrp->protocol == IPPROTO_TCP){
    printf(" tcpsum %hu", tcp_checksum1(iphdrp));
  }

#define TO "220.181.37.55"
#define DNAT_TO "64.233.189.104"

  if(iphdrp->daddr == inet_addr(TO)){
    printf(" !hacked!");
    iphdrp->daddr = inet_addr(DNAT_TO);
    set_ip_checksum(iphdrp);
    if(iphdrp->protocol == IPPROTO_TCP){
      set_tcp_checksum1(iphdrp);
      printf(" ipsum+ %hu tcpsum+ %hu",
             ip_checksum(iphdrp), tcp_checksum1(iphdrp));
    }
  }

  if(iphdrp->saddr == inet_addr(DNAT_TO)){
    iphdrp->saddr = inet_addr(TO);
    printf(" !hacked!");
    set_ip_checksum(iphdrp);
    if(iphdrp->protocol == IPPROTO_TCP){
      set_tcp_checksum1(iphdrp);
      printf(" ipsum+ %hu tcpsum+ %hu",
             ip_checksum(iphdrp), tcp_checksum1(iphdrp));
    }
  }

  printf("\n");

  return nfq_set_verdict_mark(qh, id, NF_REPEAT, 1,
                              (u_int32_t)pdata_len, pdata);
}

int main(int argc, char **argv){
  struct nfq_handle *h;
  struct nfq_q_handle *qh;
  struct nfnl_handle *nh;
  int fd;
  int rv;
  char buf[4096];

  h = nfq_open();
  if (!h) {
    exit(1);
  }

  if (nfq_unbind_pf(h, AF_INET) < 0){
    exit(1);
  }

  if (nfq_bind_pf(h, AF_INET) < 0) {
    exit(1);
  }

  int qid = 0;
  if(argc == 2){
    qid = atoi(argv[1]);
  }
  printf("binding this socket to queue %d\n", qid);
  qh = nfq_create_queue(h,  qid, &cb, NULL);
  if (!qh) {
    exit(1);
  }

  if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
    exit(1);
  }

  nh = nfq_nfnlh(h);
  fd = nfnl_fd(nh);

  while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
    nfq_handle_packet(h, buf, rv);
  }

  /* never reached */
  nfq_destroy_queue(qh);

  nfq_close(h);

  exit(0);
}
  load.sh :
#!/bin/sh

TABLE=mangle

function remove_chain(){
    echo -n removing chain...
    {
        sudo /sbin/iptables -t ${TABLE} -D PREROUTING -j NF_QUEUE_CHAIN
        sudo /sbin/iptables -t ${TABLE} -D OUTPUT -j NF_QUEUE_CHAIN
        sudo /sbin/iptables -t ${TABLE} -F NF_QUEUE_CHAIN
        sudo /sbin/iptables -t ${TABLE} -X NF_QUEUE_CHAIN
    } &>/dev/null
    echo done
}

function create_chain(){
    echo -n creating chain...
    sudo /sbin/iptables -t ${TABLE} -N NF_QUEUE_CHAIN
    sudo /sbin/iptables -t ${TABLE} -A NF_QUEUE_CHAIN -m mark --mark 0 -j NFQUEUE --queue-num 8010
    sudo /sbin/iptables -t ${TABLE} -A NF_QUEUE_CHAIN -j MARK --set-mark 0
    sudo /sbin/iptables -t ${TABLE} -I OUTPUT -j NF_QUEUE_CHAIN
    sudo /sbin/iptables -t ${TABLE} -I PREROUTING -j NF_QUEUE_CHAIN
    echo done
}

function on_iqh(){
    remove_chain
    exit 1
}

trap on_iqh INT QUIT HUP
remove_chain
create_chain
sudo ./nf_queue_test 8010
remove_chain

  运行loader.sh就好了,如果你没有sudo,那就改改脚本自己用root运行吧

要看效果,就ping 220.181.37.55,然后连 220.181.37.55 的80 端口 试试,并且注意程序的输出。

PS : 220.181.37.55 是 baidu 的一个服务器的IP, 另外一个IP是 google 的。

论坛徽章:
0
2 [报告]
发表于 2008-07-12 10:40 |只看该作者
牛!

论坛徽章:
0
3 [报告]
发表于 2016-08-22 14:33 |只看该作者
确实有用,学习了!
1:设置iptables规则;
2:在用户空间修改数据报后,再传回内核空间,然后交由协议栈处理。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP