#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <libipq.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>

#include <linux/netfilter_ipv4.h>

#define LOG(x...) fprintf(stderr,"%s(): ",__FUNCTION__) ; fprintf(stderr,##x)

#define BUFSIZE 2048
#define MAX_PACKET_SIZE 5000

int main(int argc,char *argv[])
{
  struct ipq_handle *ipq_h;
  int status,verdict,replace_size;
  unsigned char buf[BUFSIZE];
  ipq_packet_msg_t *msg;
  unsigned char new_packet_buf[MAX_PACKET_SIZE];
  int in_num,out_num;

  ipq_h=ipq_create_handle(0,PF_INET);
  if(ipq_h==NULL){
    LOG("Error creating ipq handle: %s\n",ipq_errstr());
    return 5;
  }

  if(ipq_set_mode(ipq_h,IPQ_COPY_PACKET,BUFSIZE)<0){
    LOG("Error setting ipq mode: %s\n",ipq_errstr());
    return 6;
  }

  in_num=0;
  out_num=0;

  while(1){
    replace_size=0;
    // a little bit weird; if you don't specify a timeout for ipq_read (the
    // last parameter), then it won't unblock on signals
    status=ipq_read(ipq_h,buf,BUFSIZE,0);
    if(status<0){
      LOG("Error reading from ipq: %s\n",ipq_errstr());
      return 7;
    }
    else if(status==0){
      // in this case, we don't have data available to read or we hit our
      // timeout, or we caught a signal; we don't want to do anything except
      // loop back around
      continue;
    }

    switch(ipq_message_type(buf)){
    case NLMSG_ERROR:
      LOG("Error getting message type: %s\n",strerror(ipq_get_msgerr(buf)));
      continue;
    case IPQM_PACKET:
      msg=ipq_get_packet(buf);
      if(msg==NULL){
	LOG("Error getting packet: %s\n",ipq_errstr());
	continue;
      }

      LOG("Input size: %d\n",msg->data_len);

      if(msg->hook==NF_IP_PRE_ROUTING){
	LOG("Doing prerouting\n");

	verdict=NF_ACCEPT;
	replace_size=msg->data_len;
	memcpy(new_packet_buf,msg->payload,msg->data_len);
      }
      else if(msg->hook==NF_IP_POST_ROUTING){
	LOG("Doing post routing\n");

	verdict=NF_ACCEPT;
	replace_size=msg->data_len;
	memcpy(new_packet_buf,msg->payload,msg->data_len);
      }
      else{
	// we don't know what to do with other packets; in this case, we
	// want to drop them
	LOG("Unknown hook type %d\n",msg->hook);
	verdict=NF_DROP;
	replace_size=0;
      }

      LOG("Output size: %d\n",replace_size);

      if(ipq_set_verdict(ipq_h,msg->packet_id,verdict,replace_size,new_packet_buf)<0){
	LOG("Error setting verdict: %s\n",ipq_errstr());
      }
      break;

    default:
      LOG("Unknown message type!\n");
      break;
    }
  }

  ipq_destroy_handle(ipq_h);

  return 0;
}
