/*
 *              IP_MASQ_H323, H.323 masquerading module
 *
 *
 * Version:     2.2.0 - 16 October 2000
 *
 * Author:      Rajkumar. S
 *              Archana V. S.
 *              Sheenarani I.
 *
 *              ____Released by CoRiTeL (www.coritel.it)___
 *              Luca Veltri
 *              Stefano Giacometti
 *              Raffaele Pellicciotta
 *              Paolo Iurilli
 *
 *              to work on kernel 2.2.12 and NetMeeting 3.01
 *
 *              official site: http://www.coritel.it/projects/sofia/nat.html
 *
 *      This program is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU General Public License
 *      as published by the Free Software Foundation; either version
 *      2 of the License, or (at your option) any later version.
 *
 * Fixes:
 * 	 Juan Jose Ciarlante:	coding cleanups
 *   Paolo Iurilli,Luca Veltri:
 *	 CORITEL 04.09.00: ip_masq_h225_app and ip_masq_h245_app dynamic allocation cleanups
 *   Paolo Iurilli: 
 *   CORITEL: incoming calls code integration and ip_masq_timeout_table introduction  
 *   
 */


#include <linux/module.h>
#include <asm/system.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/ip_masq.h>


#define IP_MASQ_H323_DEBUG(msg...) (1-debug<=ip_masq_get_debug_level())? printk(KERN_DEBUG "IP_MASQ:" ## msg) : 0

static int debug=0;
MODULE_PARM(debug, "i");

/*
 *  	port_h225 is set to the default h.225 port (1720)
 */
int port_h225 = 1720;

/*  CORITEL 29/09/2000 : private h323 timeout table */ 
struct ip_masq_timeout_table h323_timeout_table = {
	ATOMIC_INIT(0),		/* refcnt */
	0,			/* scale  */
	{
		30*60*HZ,	    /*      IP_MASQ_S_NONE,       */
		15*60*HZ,	    /*      IP_MASQ_S_ESTABLISHED */
		10*60*HZ,		/*      IP_MASQ_S_SYN_SENT,   */
		10*60*HZ,		/*      IP_MASQ_S_SYN_RECV,   */
		1*60*HZ,	    /*      IP_MASQ_S_FIN_WAIT,   */
		10*60*HZ,		/*      IP_MASQ_S_TIME_WAIT,  */
		5*60*HZ,		/*      IP_MASQ_S_CLOSE,      */
		5*60*HZ,		/*      IP_MASQ_S_CLOSE_WAIT, */
		10*60*HZ,		/*      IP_MASQ_S_LAST_ACK,   */
		2*60*HZ,	    /*      IP_MASQ_S_LISTEN,     */
		10*60*HZ,	    /*      IP_MASQ_S_UDP,        */
		10*60*HZ,		/*      IP_MASQ_S_ICMP,       */
		2*HZ,		    /*      IP_MASQ_S_LAST        */
	},			/* timeout */
};

/*@@@@@ private comments (CoRiTeL)...
 *  some notes:
 *  For every call from private host:
 *  1) every h.225 session regists a new 'h245 application' to handle
 *  with a new h.245 session, this 'h245 appl.' is always registered with
 *  the same ip_masq_h245 structure; for this reason only
 *  one h245 session can be handled in the same time;
 *  2) for this reason,
 *  when a new h245 session is needed, the old one is deregistrated and the
 *  'attach' value is decreased of one;
 *  For every call from public host:
 *  1) a new ip_masq entry is made for H.245 tcp connection
 *  2) a new h245 application is binded to the ip_masq entry made without
 *  registration
 */



/* CORITEL 05.09.00:
 * Move an ip_masq_app from an old port to a new port
 */
int ip_masq_move_app(struct ip_masq_app* mapp, unsigned short protocol, int new_port)
{	int n_attach_saved=mapp->n_attach;
	mapp->n_attach=0;
	if(unregister_ip_masq_app(mapp))
	{	/* deregistration failed */
		mapp->n_attach=n_attach_saved;
		return -2;
	} else
		if (register_ip_masq_app(mapp, protocol, new_port))
		{	/* registration failed */
			mapp->n_attach=n_attach_saved;
			return -1;
		} else
			{	mapp->n_attach=n_attach_saved;
				return 0;
			}
}

/*	CORITEL 11.09.00
 *	unbind ip_masq
 */

int ip_masq_unbind_app(struct ip_masq *ms)
 {
         struct ip_masq_app * mapp;
         mapp = ms->app;

         if (ms->protocol != IPPROTO_TCP && ms->protocol != IPPROTO_UDP)
                 return 0;

         if (mapp != NULL) {
                 if (mapp->masq_done_1) mapp->masq_done_1(mapp, ms);
                 ms->app = NULL;
                 mapp->n_attach--;
         }
         return (mapp != NULL);
 }
/*	CORITEL 08.09.00
 *	Bind ip_masq to a specific ip_masq_app, only for incoming calls
 */

struct ip_masq_app* ip_masq_bind_this_app(struct ip_masq* ms, struct ip_masq_app* mapp)
{
	if (mapp == NULL) return NULL;

	/* allow binding if already bound */
	if (ms->app != NULL)
	{	IP_MASQ_H323_DEBUG("ip_masq_bind_this_app() called for already bound object: old app REPLACED!\n");
        ip_masq_unbind_app(ms);
	}
	else IP_MASQ_H323_DEBUG("ip_masq_bind_this_app() called for not yet bound object!\n");
	ms->app = mapp;
        if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms);
    mapp->n_attach++;
	return mapp;
}

int h245registered=0;

struct ip_masq_app * masq_h245_app_debug;


/**********  Start of H.245 masq app functions ******************/

static int masq_h245_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
{
	/* setup the control connection with a new timeout table */
	if (!ms->timeout_table){
		          ms->timeout_table = &h323_timeout_table;
                  IP_MASQ_H323_DEBUG("masq_h245_init_1: ## NEW TIMEOUT TABLE ##\n");
			   }
	IP_MASQ_H323_DEBUG("masq_h245_init_1: IP masq app 245 init\n");
    MOD_INC_USE_COUNT;
	return 0;
}

static int masq_h245_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
{
	MOD_DEC_USE_COUNT;
	IP_MASQ_H323_DEBUG("masq_h245_done_1: IP masq app 245 done\n");
	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
	return 0;
}


int masq_h245_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
{
	IP_MASQ_H323_DEBUG("masq_h245_in\n");
	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);

	return 0;
}

int masq_h245_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
{

	struct sk_buff *skb;
	struct iphdr *iph;
	struct tcphdr *th;
	unsigned char *data, *data_limit;
	unsigned char *skbuff_p;
	__u16 data_port, *rtp_port;
	__u32 data_ip, *rtp_ip;
	struct ip_masq * n_ms_rtp;

	skb = *skb_p;
	iph = skb->h.ipiph;
	th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
	data = skb->data;
	data_limit = skb->tail;
	skbuff_p = data+12;

	IP_MASQ_H323_DEBUG("masq_h245_out\n");
	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);

	/*@@@@@ CoRiTeL: analisys of h245 msg */
	data=skb->data; 		/*@@@@@ CoRiTeL: start of IP pkt */
	data+=iph->ihl*4+th->doff*4;	/*@@@@@ CoRiTeL: jumps to app data */
	while((data+5)<data_limit) {
		data_ip=*((__u32*)data);
		if (data_ip==ms->saddr)
		{
			data_port=*((__u16*)(data+4));
			IP_MASQ_H323_DEBUG("masq_h245_out: <-- RTCP/RTP_dest: %d.%d.%d.%d:%d\n",NIPQUAD(data_ip), data_port);

			n_ms_rtp = ip_masq_new(
				IPPROTO_UDP, maddr,0,	/*@@@@@ CoRiTeL:  masq addr */
				data_ip, data_port,	/*@@@@@ CoRiTeL:  pck source */
				iph->daddr, 0,		/*@@@@@ CoRiTeL:  pck dest */
				IP_MASQ_F_NO_DPORT);
			if(n_ms_rtp==NULL)
				IP_MASQ_H323_DEBUG("masq_h245_out: RTCP/RTP masq entry not made\n");
			else {
                               ip_masq_control_add(n_ms_rtp,ms); 
                               ip_masq_listen(n_ms_rtp);
			       ip_masq_put(n_ms_rtp);
                                /* CORITEL 02/10/2000 */
			        /* setup the control connection with a new timeout table */
	                       if (!n_ms_rtp->timeout_table){
		                          n_ms_rtp->timeout_table = &h323_timeout_table;
		                          IP_MASQ_H323_DEBUG("masq_h245_out :## setup NEW TIMEOUT TABLE\n ##");
					  }	
				rtp_ip=(__u32*)data;
				rtp_port=(__u16*)(data+4);
				/*	Hack the packet  */
				*rtp_ip=n_ms_rtp->maddr;
				*rtp_port=n_ms_rtp->mport;
				IP_MASQ_H323_DEBUG("masq_h245_out: RTCP/RTP masquerated: maddr=%d.%d.%d.%d , mport=%u\n", NIPQUAD(n_ms_rtp->maddr),ntohs(n_ms_rtp->mport));
				}

		}

		data++;
	}

	return 0;
}


/***********End of H.245 app functions *******************/

struct ip_masq_app  ip_masq_h245_app = {
	NULL,			/* next */
	"h245",			/* name */
	0,                      /* type */
	0,                      /* n_attach */
	masq_h245_init_1,        /* ip_masq_init_1 */
	masq_h245_done_1,        /* ip_masq_done_1 */
	masq_h245_out,           /* pkt_out */
	masq_h245_in,            /* pkt_in */
};


static int masq_h225_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
{	IP_MASQ_H323_DEBUG("masq_h225_init_1\n");
    /* setup the control connection with a new timeout table */
	if (!ms->timeout_table){
		      ms->timeout_table = &h323_timeout_table;
              IP_MASQ_H323_DEBUG("masq_h225_init_1: ## NEW TIMEOUT TABLE ##\n");
			  }
	MOD_INC_USE_COUNT;
	return 0;
}

static int masq_h225_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
{
	IP_MASQ_H323_DEBUG("masq_h225_done_1\n");
	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);
	IP_MASQ_H323_DEBUG("masq_h225: h245 status (0=unregistered, 1=registered) = %d\n",h245registered); /*@@@@@ CoRiTeL: debug*/
	MOD_DEC_USE_COUNT;
	return 0;
}

int masq_h225_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
{   /* CORITEL 08.09.00: */
	struct sk_buff *skb;
	struct iphdr *iph;
	struct tcphdr *th;
	unsigned char *data, *data_limit;
	unsigned char *skbuff_p;
	__u16 data_port, *h245_port;
	__u32 data_ip, *h245_ip;
	struct ip_masq *n_ms;
	int f_alarm; /*CORITEL 06/10/2000: patch for voxilla, unnecessary for netmeeting and perhaps other application, 
	                  voxilla makes some false alarm */
	IP_MASQ_H323_DEBUG("masq_h225_out:\n");
	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);

		skb = *skb_p;
		iph = skb->h.ipiph;
		th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
		data = skb->data;
		data_limit = skb->tail;
		skbuff_p = data+12;
	    /* CORITEL: analisys of h225 msg */
		data=skb->data;
		data+=iph->ihl*4+th->doff*4;
                f_alarm=0;
		while((data+5)<data_limit) {
			data_ip=*((__u32*)data);
			if (data_ip==ms->saddr)
			{
				data_port=*((__u16*)(data+4));
				/* CORITEL: incoming call from private host: substitution addr and port field in payload msg only */  
				if(data_port==ms->sport)
					{	h245_ip=(__u32*)data;
						h245_port=(__u16*)(data+4);
                        /*	Hack the packet  */
				      	*h245_ip=ms->maddr;
					    *h245_port=ms->mport;
					     f_alarm=1;/* patch for voxilla */
                                             IP_MASQ_H323_DEBUG("masq_h225_out: call from private host substitution addr and port field/n"); 
                    }
				else if(!f_alarm)    /* CORITEL: incoming call from public host: new ip_masq entry  */  
				{	   IP_MASQ_H323_DEBUG("masq_h225_out: private host:port <--  %d.%d.%d.%d:%d\n",NIPQUAD(data_ip), data_port);
				       IP_MASQ_H323_DEBUG("masq_h225_out:It means that the call is incoming from public host!\n");
		               
					   n_ms = ip_masq_new(
	                                   IPPROTO_TCP, maddr,0,
					   data_ip, data_port,
					   iph->daddr, 0,
					   IP_MASQ_F_NO_DPORT);
				       if(n_ms==NULL)
				            	IP_MASQ_H323_DEBUG("masq_h225_out: h245 ip_masq entry not made\n");
				       else {    ip_masq_control_add(n_ms,ms); 
                                                 ip_masq_listen(n_ms);
			 			 ip_masq_put(n_ms);
						 /* CORITEL 02/10/2000 */
						 /* setup the control connection with a new timeout table */
	                                         if (!n_ms->timeout_table){
		                                         n_ms->timeout_table = &h323_timeout_table;
				                         IP_MASQ_H323_DEBUG("masq_h225_out : ## NEW TIMEOUT TABLE ##\n");	
				                  }
						  h245_ip=(__u32*)data;
					             h245_port=(__u16*)(data+4);
					             /*	Hack the packet  */
					             *h245_ip=n_ms->maddr;
					             *h245_port=n_ms->mport;
				                 IP_MASQ_H323_DEBUG("masq_h225_out: masquerated: maddr=%d.%d.%d.%d , mport=%u\n", NIPQUAD(n_ms->maddr),ntohs(n_ms->mport));
				                 ip_masq_bind_this_app(n_ms,&ip_masq_h245_app); /* no registration of ip_masq_h245_app */
				             }       
                }  
			}
			data++;
		}

	return 0;
}

int masq_h225_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
{
	struct sk_buff *skb;
	struct iphdr *iph;
	struct tcphdr *th;
	unsigned char *data, *data_limit;
	__u32 source_ip,m_ip;
	__u16 port;
	__u32 temp_ip,*h225_ip;
	unsigned char *skbuff_p;
	unsigned char p1,p2;
	int n_attach_saved;
	int result;

	IP_MASQ_H323_DEBUG("masq_h225_in\n");
	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);

	skb = *skb_p;
	iph = skb->h.ipiph;
	th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
	data = skb->data;
	data_limit = skb->tail;
	skbuff_p = data+12;
	source_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
	skbuff_p=skbuff_p+4;
	temp_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
	
	while(((skbuff_p+6)< data_limit) && (temp_ip !=source_ip)){
		skbuff_p++;
		temp_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
	}
	if(temp_ip==source_ip){
		p1=*(skbuff_p+4);
		p2=*(skbuff_p+5);
		port=(p1<<8)+p2;

		IP_MASQ_H323_DEBUG("masq_h225_in: H.245 Port is at: %u\n",port);

		/* CORITEL 05.09.00: bind the h245_app on the new port:
		   - first, deregister the old ip_masq_h245_app,
		   - then, register it on the new port.
        */
		if (h245registered)
		{	IP_MASQ_H323_DEBUG("masq_h225_in: found an old ip_masq_app entry for h245, I'm try to deregist it and regist the new port...\n");
		        result=ip_masq_move_app(&ip_masq_h245_app,IPPROTO_TCP,port);
			switch (result)
			{	case -2 : IP_MASQ_H323_DEBUG("masq_h225_in: deregistration failed\n"); break;
				case -1 : IP_MASQ_H323_DEBUG("masq_h225_in: deregistration OK, registration failed\n"); h245registered--; break;
				case 0 : IP_MASQ_H323_DEBUG("masq_h225_in: registration OK\n");
			}
		}
		else
			{	n_attach_saved=ip_masq_h245_app.n_attach;
				if (register_ip_masq_app(&ip_masq_h245_app, IPPROTO_TCP, port)==0)
				{	/* registration OK */
					h245registered=1;
					IP_MASQ_H323_DEBUG("masq_h225_in: registration of new h245_app OK\n");
				} else
					IP_MASQ_H323_DEBUG("masq_h225_in: registration of new h.245 _app failed\n");
				ip_masq_h245_app.n_attach=n_attach_saved;
			}
	}
	/* CORITEL 26/09/2000 */
	/* CORITEL: incoming call from public host: substitution  */  
	skbuff_p=data+16;
	m_ip=ntohl(ms->maddr);
	while(((skbuff_p+6)< data_limit) && (temp_ip!=m_ip)){
	        skbuff_p++;
		    temp_ip= (((*skbuff_p) << 24) + (*(skbuff_p+1)<< 16) + (*(skbuff_p+2) << 8) + (*(skbuff_p+3)));
	}
	if (temp_ip==m_ip)
	    {   /*	Hack the packet  */ 
		    h225_ip=(__u32*)skbuff_p;
			*h225_ip=ms->saddr;
		}
	return 0;
}

struct ip_masq_app ip_masq_h225_app = {
	NULL,			/* next */
	"h225",			/* name */
	0,                      /* type */
	0,                      /* n_attach */
	masq_h225_init_1,        /* ip_masq_init_1 */
	masq_h225_done_1,        /* ip_masq_done_1 */
	masq_h225_out,           /* pkt_out */
	masq_h225_in,            /* pkt_in */
};

/*
 * 	ip_masq_h225 initialization
 */

int ip_masq_h225_init(void)
{
	IP_MASQ_H323_DEBUG("ip_masq_h225_init\n"); 
	if (register_ip_masq_app(&ip_masq_h225_app, IPPROTO_TCP, port_h225))
		IP_MASQ_H323_DEBUG("ip_masq_h225_init: H323: ERROR loading h225 support on port = %d\n", port_h225);
		else IP_MASQ_H323_DEBUG("ip_masq_h225_init: H323: loaded h225 support on port = %d\n", port_h225);
	return 0;
}

/*
 * 	ip_masq_h225 fin.
 */

int ip_masq_h225_done(void)
{	int n_attach_saved;

	IP_MASQ_H323_DEBUG("ip_masq_h225_done\n");
	IP_MASQ_H323_DEBUG("masq_h245_app.n_attach= %d\n",masq_h245_app_debug->n_attach);

	if (unregister_ip_masq_app(&ip_masq_h225_app))
		IP_MASQ_H323_DEBUG("ip_masq_h225_done: H323: ERROR unloading support on port = %d\n", port_h225);
		else IP_MASQ_H323_DEBUG("ip_masq_h225_done: H323: unloaded support on port = %d\n", port_h225);

	if (h245registered) {
		IP_MASQ_H323_DEBUG("ip_masq_h225_done: removing the masq app entry\n");
		n_attach_saved=ip_masq_h245_app.n_attach;
		unregister_ip_masq_app(&ip_masq_h245_app);
		ip_masq_h245_app.n_attach=n_attach_saved;
		h245registered=0;
	}
	return 0;
}

#ifdef MODULE

int init_module(void)
{
	IP_MASQ_H323_DEBUG("Init module\n");

	/* CORITEL: DEBUG: */
	masq_h245_app_debug=&ip_masq_h245_app;

	if (ip_masq_h225_init() != 0)
		return -EIO;
	return 0;
}

void cleanup_module(void)
{
	if (ip_masq_h225_done() != 0)
		IP_MASQ_H323_DEBUG("ip_masq_h225: can't remove module\n");
}

#endif /* MODULE */
