用netfilter_queue在用户态实现NAT

偶尔在网上看到了<<用netfilter_queue 在用户态修改网络数据包的例子程序>>这篇文章,
并结合libnetfilter_queue-0.0.17.tar.bz2中的例子,
然后修改了一下tcp计算checksum部分,在linux2.6.24上用netfilter_queue在用户态实现NAT

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

直接上代码:

nf_queue_test.c

/*
 * =====================================================================================
 *
 * Filename: nf_queue_test.c
 *
 * Description: 用netfilter_queue 在用户态修改网络数据包的例子程序
 *
 * Version: 1.0
 * Created: 04/02/2010 09:49:48 AM
 * Revision: none
 * Compiler: gcc
 *
 * Author: LeiuX (xulei), xulei@pact518.hit.edu.cn
 * Company: HIT
 *
 * =====================================================================================
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <string.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

struct tcp_pseudo /*the tcp pseudo header*/
{
  __u32 src_addr;
  __u32 dst_addr;
  __u8 zero;
  __u8 proto;
  __u16 length;
} pseudohead;


long checksum(unsigned short *addr, unsigned int count) {
  /* Compute Internet Checksum for "count" bytes
   * beginning at location "addr".
   */

  register long sum = 0;

  while( count > 1 ) {
    /* This is the inner loop */
    sum += * addr++;
    count -= 2;
  }
  /* Add left-over byte, if any */
  if( count > 0 )
    sum += * (unsigned char *) addr;

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

  return ~sum;
}


/*************************tcp checksum**********************/
long get_tcp_checksum(struct iphdr * myip, struct tcphdr * mytcp) {

  __u16 total_len = ntohs(myip->tot_len);

  int tcpopt_len = mytcp->doff*- 20;
  int tcpdatalen = total_len - (mytcp->doff*4) - (myip->ihl*4);

  pseudohead.src_addr=myip->saddr;
  pseudohead.dst_addr=myip->daddr;
  pseudohead.zero=0;
  pseudohead.proto=IPPROTO_TCP;
  pseudohead.length=htons(sizeof(struct tcphdr) + tcpopt_len + tcpdatalen);

  int totaltcp_len = sizeof(struct tcp_pseudo) + sizeof(struct tcphdr) + tcpopt_len +tcpdatalen;
  //unsigned short * tcp = new unsigned short[totaltcp_len];

  unsigned short * tcp = malloc(totaltcp_len);


  memcpy((unsigned char *)tcp,&pseudohead,sizeof(struct tcp_pseudo));
  memcpy((unsigned char *)tcp+sizeof(struct tcp_pseudo),(unsigned char*)mytcp,sizeof(struct tcphdr));
  memcpy((unsigned char *)tcp+sizeof(struct tcp_pseudo)+sizeof(struct tcphdr),(unsigned char *)myip+(myip->ihl*4)+(sizeof(struct tcphdr)), tcpopt_len);
  memcpy((unsigned char *)tcp+sizeof(struct tcp_pseudo)+sizeof(structtcphdr)+tcpopt_len, (unsigned char *)mytcp+(mytcp->doff*4), tcpdatalen);

  /* printf("pseud length: %d\n",pseudohead.length);
          printf("tcp hdr length: %d\n",mytcp->doff*4);
          printf("tcp hdr struct length: %d\n",sizeof(struct tcphdr));
          printf("tcp opt length: %d\n",tcpopt_len);
          printf("tcp total+psuedo length: %d\n",totaltcp_len);

          fflush(stdout);

          printf("tcp data len: %d, data start %u\n", tcpdatalen,mytcp + (mytcp->doff*4));
   */



  return checksum(tcp,totaltcp_len);

}

static u_int16_t tcp_checksum(struct iphdr* iphdrp){
  struct tcphdr *tcphdrp =
    (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));
  return get_tcp_checksum(iphdrp, tcphdrp);
}

static void set_tcp_checksum(struct iphdr* iphdrp){
  struct tcphdr *tcphdrp =
    (struct tcphdr*)((u_int8_t*)iphdrp + (iphdrp->ihl<<2));
  tcphdrp->check = 0;
  tcphdrp->check = get_tcp_checksum(iphdrp, tcphdrp);
}
/****************************tcp checksum end****************************/


/********************************Ip checksum*****************************/
static u_int16_t ip_checksum(struct iphdr* iphdrp){
  return checksum((unsigned short*)iphdrp, iphdrp->ihl<<2);
}

static void set_ip_checksum(struct iphdr* iphdrp){
  iphdrp->check = 0;
  iphdrp->check = checksum((unsigned short*)iphdrp, iphdrp->ihl<<2);
}
/****************************Ip checksum end******************************/

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_checksum(iphdrp));
  }

#define TO "220.181.37.55"
#define DNAT_TO "202.118.236.130"

  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_checksum(iphdrp);
      printf(" ipsum+ %hu tcpsum+ %hu",
          ip_checksum(iphdrp), tcp_checksum(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_checksum(iphdrp);
      printf(" ipsum+ %hu tcpsum+ %hu",
          ip_checksum(iphdrp), tcp_checksum(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);
  }

  nfq_unbind_pf(h, AF_INET);

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


  /*
     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);
}


Makefile

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

clean :
    rm -f nf_queue_test


load.sh

#!/bin/bash
#===============================================================================
#
# FILE: load.sh
# 
# USAGE: ./load.sh 
# 
# DESCRIPTION: 
# 
# OPTIONS: ---
# REQUIREMENTS: ---
# BUGS: ---
# NOTES: ---
# AUTHOR: LeiuX (), marksman.xu@gmail.com
# COMPANY: HIT
# VERSION: 1.0
# CREATED: 04/02/2010 09:51:36 AM CST
# REVISION: ---
#===============================================================================

#!/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是 pact lab 的.

reference:
<<用netfilter_queue 在用户态修改网络数据包的例子程序>>
libnetfilter_queue-0.0.17.tar.bz2
tcp计算checksum
<<tcp/ip详解卷一:协议>>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值