Wednesday 8 August 2012

NAT Implementation using NETFILTER in Linux Kernel (Ver: 2.6.34.10)

NAT (Network Address Translation)

In computer networking, network address translation (NAT) is the process of modifying IP address information in IP packet headers.
With the help of NAT, private  IP address can be translated to public IP address, or in other words it hides the private IP address space from public IP address space.

In my previous post, you would have seen about the NETFILTER basics.
In this post, I would like to explain about the NAT implementation using NETFILTER code.

While implementing NAT in NETFILTER,  DNAT should perform in pre-Routing hook whereas SNAT( Source NAT) should perform in post-Routing Hook ( Remember this is a Basic Thumb rule)
In order to send the packet  from Pre-Routing Hook to Post-Routing Hook, the Forward Hook should be enabled as follows.
      
vim /proc/sys/net/ipv4/ip_forward
change this ip_forward =0 to 1
echo 1 > /proc/sys/net/ipv4/ip_forward

Note: DNAT and SNAT cannot be performed in pre-Routing Hook ( This will not work). 

Test setup
========



NAT can be implemented by using two Linux Machines. From one Linux machine(10.12.7.95) send the UDP traffic to another Linux Machine(10.12.7.75) where kernel module(.ko) resides.

The result is, the same Traffic will be received in 10.12.7.95.


Netfilter Kernel Source Code: (fileName: muniTestNetfilter.c)


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
#include <linux/netlink.h>
#include <linux/udp.h>

/* Optional Headers  not Required, If you don't want Remove*/
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/socket.h>
#include <linux/icmp.h>

/* These are Required for NETFILTER functionality */
#include <net/checksum.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/interrupt.h>


#define DRIVER_AUTHOR_NAME  "MUNI"
#define DRIVER_DESCRIPTION  "NETFILTER"

#define DEST_PORT_NUM      2905

/* NETFILTER*/
static struct nf_hook_ops test_hookPreRouting;
static struct nf_hook_ops test_HookPostRouting;
/*
 * preRoutingHookEntryFunc -> Pre Routing Function
 */
static unsigned int preRoutingHookEntryFunc(
                       unsigned int unHookNum,
                       struct sk_buff *skb,
                       const struct net_device *inDevice,
                       const struct net_device *outDevice,
                       int (*okfn)(struct sk_buff *))
{
    /* Copy the Socket Buffer */
    struct sk_buff *sockBuff = skb;

    /* Intialise  the UDP Header  */
    struct udphdr *uh = NULL;

    /* Extract the IP Header */
    struct iphdr *iph = ip_hdr(skb);

    /* If the IP hdr is Zero or SockBuff is Zero Just Accept */
    if(NULL == sockBuff || NULL == iph)
    return NF_ACCEPT;

    /* Point to the UDP Header */
    uh = (struct udphdr*)(sockBuff->data + (iph->ihl * 4));

    /* Filter only UDP Packet */
    if((iph->protocol == IPPROTO_UDP) &&
                       (ntohs(uh->dest) == DEST_PORT_NUM))
    {
        /* Retrieve Udp Header */
        printk(KERN_INFO " Recived Packet Before [ PRE HOOK ] with  IPDest:%x:PortNum:%d"
                "IPSrc%x:PortNum:%d\n",
               ntohl(iph->daddr),ntohs(uh->dest),
               ntohl(iph->saddr),ntohs(uh->source));
    

    /* Perform the DNAT in Pre-Routing */
    /* Change Dest IP address and mention appropriate address */
    iph->daddr = htonl(0x0a0c075f);


        printk(KERN_INFO " Recived Packet After [ PRE HOOK ] with  IPDest:%x:PortNum:%d"
                "IPSrc%x:PortNum:%d\n",
               ntohl(iph->daddr),ntohs(uh->dest),
               ntohl(iph->saddr),ntohs(uh->source));
   
    }
   /*  All  Packets Just NF_ACCEPT
    *  If it is LOCAL packets then it will go to LOCAL_IN
    *  to Application else for NON LOCAL Packets it will go
    *  POST HOOK and Out
    */
    return NF_ACCEPT;
}
/*
 * postRoutingHookEntryFunc -> Post Routing Function
 *
 * POST HOOK will be called for NON LOCAL packets from
 *  PRE_HOOK- POST HOOK via FORWARD
 * else LOCAL_OUT - POST HOOK for Local Generated Packets from the system(PING)
 *
 */
static unsigned int postRoutingHookEntryFunc(
        unsigned int unHookNum,
        struct sk_buff *skb,
        const struct net_device *inDevice,
        const struct net_device *outDevice,
        int (*okfn)(struct sk_buff *))
{
   /* Copy the Socket Buffer */
    struct sk_buff *sockBuff = skb;

    /* Intialise  the UDP Header  */
    struct udphdr *uh = NULL;

    /* Extract the IP Header */
    struct iphdr *iph = ip_hdr(skb);


    /* If the IP hdr is Zero or SockBuff is Zero Just Accept */
    if(NULL == sockBuff || NULL == iph)
    return NF_ACCEPT;

    /* Point to the UDP Header */
    uh = (struct udphdr*)(sockBuff->data + (iph->ihl * 4));


   /* Filter only UDP Packet */
   if((iph->protocol == IPPROTO_UDP) &&
                     (ntohs(uh->dest) == DEST_PORT_NUM))
    {
        /* Retrieve Udp Header */
        printk(KERN_INFO " Recived Packet In BEFORE [ POST HOOK ]  IPDest:%x:PortNum:%d"
                "IPSrc%x:PortNum:%d\n",
               ntohl(iph->daddr),ntohs(uh->dest),
               ntohl(iph->saddr),ntohs(uh->source));
   
     /* Perform the SNAT in POST-Routing */

    /* Change SourceIP address and mention appropriate address */
    iph->saddr = htonl(0xa0c074b);
   
        /* Retrieve Udp Header */
        printk(KERN_INFO " Recived Packet In AFTER [ POST HOOK ]  IPDest:%x:PortNum:%d"
                "IPSrc%x:PortNum:%d\n",
               ntohl(iph->daddr),ntohs(uh->dest),
               ntohl(iph->saddr),ntohs(uh->source));
    }

    /*  All  Packets Just do NF_ACCEPT
     *  then packets  will go Out of the Driver
    */
    return NF_ACCEPT;
}
/*
 * Module Init Function
 */
static int __init muni_test_netfilter_init(void)
{
    int unReturn = 0;

    printk(KERN_INFO "Loading MUNI NETFILTER Module\n");

    /* Initialization of Pre Hook Parameters */

    /* Which Look You need to Call */
    test_hookPreRouting.hooknum  = NF_IP_PRE_ROUTING;
    /* Type of Family */
    test_hookPreRouting.pf       = PF_INET;
    /* Call back function for Prehook */
    test_hookPreRouting.hook     = preRoutingHookEntryFunc;
    /* Priority means, Which module receives the packet first
    * Here Priority = FIRST, means this module receives the packet first.
     */
    test_hookPreRouting.priority = NF_IP_PRI_FIRST;

    /* Register the Pre Routing Hook in Kernel */
    if((unReturn = nf_register_hook(&test_hookPreRouting)) < 0)
    {
        printk(KERN_INFO "Registration of Pre Routing"
                                " Hook is failed and ERROR=%d\n", unReturn);

        /* Un Register the Pre Routing Hook */
        nf_unregister_hook(&test_hookPreRouting);
        return unReturn;
    }

    /* Intialisation of Post Hook Parameters */
    test_HookPostRouting.hooknum = NF_IP_POST_ROUTING;
    test_HookPostRouting.pf      = PF_INET;
    /* Call back function for Post Hook  */
    test_HookPostRouting.hook    = postRoutingHookEntryFunc;

    /* Priority means, Which module receives the packet first
     * Here Priority = FIRST, means this module recives the packet first.
     */
    test_HookPostRouting.priority= NF_IP_PRI_FIRST;

    /* Register the Post Routing Hook in Kernel */
    if((unReturn = nf_register_hook(&test_HookPostRouting)) < 0)
    {
        printk(KERN_INFO "Registration of Post Routing"
                                "Hook is Failed and ERROR=%d\n", unReturn);
        /* Un Register the Post Routing Hook */
        nf_unregister_hook(&test_HookPostRouting);
        return unReturn;
    }

    return 0;
}

/*
 * Module Exit Function
 */
static void __exit muni_test_netfilter_exit(void)
{
    printk(KERN_INFO "UnLoading MUNI NETFILTER Module\n");

    /* Unregister the Pre Routing Hook */
    nf_unregister_hook(&test_hookPreRouting);

    /* Unregister the Post Routing Hook */
    nf_unregister_hook(&test_HookPostRouting);

}
/* Init Module */
module_init(muni_test_netfilter_init);


/* Exit Module */
module_exit(muni_test_netfilter_exit);

/* Some MODULE PARAMETERS */
MODULE_LICENSE("COPY");
MODULE_AUTHOR(DRIVER_AUTHOR_NAME);
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);

MODULE_VERSION("2.6.34.10");

Kernel Make file
obj-m += natTest.o
natTest-objs :=  muniTestNetfilter.o
all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean




User Space code  ( Used for send and Receive UDP traffic).
============

#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>

#define BUFFSIZE 5096

int sendlen, receivelen;
int i,count, sentCnt=0;
unsigned char buffer[BUFFSIZE];
/* client Socket */
struct sockaddr_in receivesocket;
/* Server Socket */
struct sockaddr_in sendsocket;

/* Sock FD */
int sockFd;

/* Number of Times you need */
unsigned int ch;
unsigned int noOfTimes;

/* Send UDP Data */
int sendUDPData();

/* Receive Call Back Function */
void *recvNetfilterData(void *);


int main(int argc, char *argv[]) {
int ret = 0;

   /* Create the UDP socket */
   if ((sockFd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
           perror("socket");
           return -1;
   }

   /* my address or Parameters ( These are required for Binding the Port and
        IP Address )
     Bind to my own port and Address */
   memset(&receivesocket, 0, sizeof(receivesocket));
   receivesocket.sin_family = AF_INET;
   receivesocket.sin_addr.s_addr = htonl(INADDR_ANY);
    receivesocket.sin_port = htons(2905);

   receivelen = sizeof(receivesocket);

   /* Bind the my Socket */
   if (bind(sockFd, (struct sockaddr *) &receivesocket, receivelen) < 0) {
           perror("bind");
           return -1;
   }

   /* Server address or Parameters , Sending System Address and Port Num */
   memset(&sendsocket, 0, sizeof(sendsocket));
   sendsocket.sin_family = AF_INET;
   /* give Proper IP address */
   sendsocket.sin_addr.s_addr = inet_addr("10.12.7.75");
   sendsocket.sin_port = htons(2905);

   /* Create Seperate Thread */
   /* Start the Receiving Thread */
   pthread_t threadId;
   if(pthread_create(&threadId, NULL, recvNetfilterData, NULL))
   {
       printf("Error in creating receiver thread\n");
       return 0;
   }


   /* Do Loop -> Send UDP Data */
   do
    {
       printf(" Enter your choice:\t\n");
       printf(" 1. Send UDP Data \n");
       printf(" 2. exit \n");
       scanf("%d", &ch);
       printf("\n");

       switch(ch)
       {

           case 1:
                   printf("Enter the Length of the Payload \n");
                   scanf("%d", &sendlen);
                   printf("Enter How many times you want to send data \n");
                          scanf("%d", &noOfTimes);
                   /* Send UDP Data */
                   sendUDPData();
                   break;

           default:
                  printf("Invalid Choice\n");
                  break;
       }
 }while(ch!=2);
return 0;
}
/*
 *  sendUDPData
 */
int sendUDPData()
{
        int count=0;
        memset(buffer, 31, sendlen);

        for(count=0; count< noOfTimes;  count++)
        {
           /* Send the UDP Data */
           if (sendto(sockFd, buffer, sendlen, 0,
                        (struct sockaddr*)&sendsocket, sizeof(sendsocket)) != sendlen)
           {
                perror("sendto");
                return -1;
           }
           else
           {
                sentCnt++;
           }
         }
    return 0;
}
/*
 * Dump Data
 */
void dumpData(unsigned char *data,  unsigned int len)
{
    unsigned int uIndx;
     if(data)
     {
        for(uIndx=0; uIndx<len; ++uIndx)
        {
           if(uIndx%32 == 0)
           {
              printf("\n%4d:", uIndx);
           }
           if(uIndx%4 == 0)
           {
                 printf(" ");
           }
           printf("%02x", data[uIndx]);
        }
    }
    printf(" Length of Bytes: %d\n", len);
    printf("\n");
}

/*
 * recvNetfilterData()
 */
void *recvNetfilterData(void *args)
{
    unsigned char buf[5096];
    int receivedLen = 0;

    while(1)
    {
        memset(buf, 0, BUFFSIZE);
        /* Recieve the Data from Other system */
        if ((receivedLen = recvfrom(sockFd, buf, BUFFSIZE, 0, NULL, NULL)) < 0)
        {
                perror("recvfrom");
                return 0;
        }
        else if(receivedLen == 0)
        {
             printf( " The Return Value is 0 \n");
        }
        else
        {
              /* Print The data */
             printf( " Recvd Byte length %d\n:", receivedLen );
             dumpData(buf, receivedLen);
        }
    }
}






























































































































6 comments: