#include <stdio.h>
#include <assert.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/errno.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/ethernet.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <string.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <pthread.h>
#include <sys/param.h>

#include "grpAddr.h"
//#include "ipaddr.h"

#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define BAUDRATE        B38400//B115200
#define MODEMDEVICE     "/dev/ttyS0"

#define BROADCAST       0xffff
#define BROADCASTON     0xfffd
#define BROADCASTCR     0xfffc
#define GETSRC_MAC      0x00
#define GETPANID        0x01
#define GETSERVER       0x02
#define UNICAST         0x10
#define GROUPCAST       0x11



int ip_id = 0;	/* to have my own ip_id creates collision with kernel ip->id
		** but it should be ok because the packets are unlikely to be
		** fragmented (they are non routable and small) */
		/* WORK: this packet isnt routed, i can check the outgoing MTU
		** to warn the user only if the outoing mtu is too small */
static char vrrp_hwaddr[6];	// WORK: lame hardcoded for ethernet
static vrrp_rt	glob_vsrv;
static char	PidDir[FILENAME_MAX+1];


int failure = 0; 
int failCount;


/*==============================================================
 return IP address by ifname
==============================================================*/
static uint32_t ifname_to_ip( char *ifname )
{
	struct ifreq	ifr;
	int		fd	= socket(AF_INET, SOCK_DGRAM, 0);
	uint32_t	addr	= 0;
	if (fd < 0) 	return (-1);
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	if (ioctl(fd, SIOCGIFADDR, (char *)&ifr) == 0) {
		struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
		addr = ntohl(sin->sin_addr.s_addr);
	}
	close(fd);
	return addr;
}

/*==============================================================
 copy hardware address to addr by ifname
==============================================================*/
static int hwaddr_get( char *ifname, char *addr, int addrlen )
{
	struct ifreq	ifr;
	int		fd	= socket(AF_INET, SOCK_DGRAM, 0);
	int		ret;
	if (fd < 0) 	return (-1);
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	ret = ioctl(fd, SIOCGIFHWADDR, (char *)&ifr);
	memcpy( addr, ifr.ifr_hwaddr.sa_data, addrlen );
//printf("%x:%x:%x:%x:%x:%x\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] );
	close(fd);
	return ret;
}

/***************************************************************
 authentication
***************************************************************/
static int parse_authopt(vrrp_rt *vsrv, char *str)
{
 	int	len	= strlen(str);
	vrrp_if *vif	= &vsrv->vif;

	vif->auth_type = VRRP_AUTH_NONE;
	/* epxlicitly no authentication */
	if( !strcmp(str,"none") )	return 0;
	/* below the min len */
 	if( len < 5 )		return -1;
	/* check the type of authentication */
 	if( !strncmp(str, "ah/0x", 5 ) ){
		vif->auth_type = VRRP_AUTH_AH;	
		return -1;	/* WORK: not yet implemented */
	}else if( !strncmp(str, "pw/0x", 5 ) ){
		int	i,j;
		vif->auth_type = VRRP_AUTH_PASS;
		memset( vif->auth_data, 0, sizeof(vif->auth_data) );
		/* parse the key */
		for( i=5,j=0; j < sizeof(vif->auth_data)*2 && i<len ;i++,j++ ){
			/* sanity check */
			if( !isxdigit(str[i]) )	return -1;
			str[i] = toupper(str[i]);
			if( str[i] >= 'A' )	str[i] -= 'A' - 10;
			else			str[i] -= '0';
			vif->auth_data[j/2] |= str[i] << (j&1 ? 0 : 4 );
//WORK:			printf(" j=%d c=%d 0x%x\n",j,str[i],vif->auth_data[j/2]);
		}
		if( i != len )	return -1;
	}else{
		return -1;
	}
	return(0);
}

/*==============================================================
 display the usage
==============================================================*/
static void usage( void )
{
	fprintf( stderr, "vrrpd version %s\n", VRRPD_VERSION );
	//fprintf( stderr, "Usage: vrrpd -i ifname -v vrid [-f piddir] [-s] [-a auth] [-p prio] [-nh] ipaddr\n" );
	fprintf( stderr, "Usage: vrrpd -i ifname -v vrid [-f piddir] [-s] [-a auth] [-p prio] [-nh]\n" );
	fprintf( stderr, "  -h       : display this short inlined help\n" );
	//fprintf( stderr, "  -n       : Dont handle the virtual mac address\n" );
	fprintf( stderr, "  -i ifname: the interface name to run on\n" );
	fprintf( stderr, "  -v vrid  : the id of the virtual server [1-255]\n" );
	fprintf( stderr, "  -s       : Switch the preemption mode (%s by default)\n"
				, VRRP_PREEMPT_DFL? "Enabled" : "Disabled" );
	fprintf( stderr, "  -a auth  : (not yet implemented) set the authentification type\n" );
	fprintf( stderr, "             auth=(none|pass/hexkey|ah/hexkey) hexkey=0x[0-9a-fA-F]+\n");
	fprintf( stderr, "  -p prio  : Set the priority of this host in the virtual server (dfl: %d)\n"
							, VRRP_PRIO_DFL );
	fprintf( stderr, "  -f piddir: specify the directory where the pid file is stored (dfl: %s)\n"
							, VRRP_PIDDIR_DFL );
	fprintf( stderr, "  -d delay : Set the advertisement interval (in sec) (dfl: %d)\n"
							, VRRP_ADVER_DFL );
	//fprintf( stderr, "  ipaddr   : the ip address(es) of the virtual server\n" );
}

/*==============================================================
 parse the command line
================================================================================================*/
static int parse_cmdline( vrrp_rt *vsrv, int argc, char *argv[] )
{
	vrrp_if *vif = &vsrv->vif;
	int	c;
	while( 1 ){
		//c = getopt( argc, argv, "f:si:v:a:p:d:hn" );
		c = getopt( argc, argv, "f:si:v:a:p:d:h" );
		/* if the parsing is completed, exit */
		if( c == EOF )	break;
		switch( c ){
		/*case 'n':
			vsrv->no_vmac	= 1;
			break;*/
		case 's':
			vsrv->preempt	= !vsrv->preempt;
			break;
		case 'f':
			snprintf( PidDir, sizeof(PidDir), "%s", optarg );
			break;
		case 'i':
			vif->ifname	= strdup( optarg );
			/* get the ip address by ifname*/
			vif->ipaddr	= ifname_to_ip( optarg );
			if( !vif->ipaddr ){
				fprintf( stderr, "no interface found!\n" );
				goto err;
			}
			/* get the hwaddr by vif->ifname and fill it in vif->hwaddr*/
			if( hwaddr_get( vif->ifname, vif->hwaddr
					, sizeof(vif->hwaddr)) ){
				fprintf( stderr, "Can't read the hwaddr on this interface!\n" );
				goto err;
			}
//			printf("ifname=%s 0x%x\n",vsrv->vif.ifname,vsrv->vif.ipaddr);
			break;
		case 'v':
			vsrv->vrid = atoi( optarg );
			if( VRRP_IS_BAD_VID(vsrv->vrid) ){
				fprintf( stderr, "bad vrid!\n" );
				goto err;
			}
			vrrp_hwaddr[0] = 0x00;
			vrrp_hwaddr[1] = 0x00;
			vrrp_hwaddr[2] = 0x5E;
			vrrp_hwaddr[3] = 0x00;
			vrrp_hwaddr[4] = 0x01;
			vrrp_hwaddr[5] = vsrv->vrid;
			break;
		case 'a':
			if( parse_authopt( vsrv, optarg ) ){
				fprintf( stderr, "Invalid authentication key!\n" );
				goto err;
			}
			break;
		case 'p':
			vsrv->priority = atoi( optarg );
			if( VRRP_IS_BAD_PRIORITY(vsrv->priority) ){
				fprintf( stderr, "bad priority!\n" );
				goto err;
			}
			break;
		case 'd':
			vsrv->adver_int = atoi( optarg );
			//if( VRRP_IS_BAD_ADVERT_INT(vsrv->adver_int) ){
			//	fprintf( stderr, "bad advert_int!\n" );
			//	goto err;
			//}
			vsrv->adver_int *= VRRP_TIMER_HZ;
			break;
		case 'h':
			usage();
			exit( 1 );			break;
		case ':':	/* missing parameter */
		case '?':	/* unknown option */
		default:
			goto err;
		}
	}
	return optind;
err:;
	usage();
	return -1;
}

/*==============================================================
 
==============================================================*/
static void cfg_add_ipaddr( vrrp_rt *vsrv, uint32_t ipaddr )
{
	//vsrv->naddr++;
	/* alloc the room */

	if( vsrv->vaddr ){
		/*vsrv->vaddr = realloc( vsrv->vaddr
					, vsrv->naddr*sizeof(*vsrv->vaddr) );*/
		vsrv->vaddr = realloc( vsrv->vaddr
					, sizeof(*vsrv->vaddr) );
	} else {
		vsrv->vaddr = malloc( sizeof(*vsrv->vaddr) );
	}
	assert( vsrv->vaddr );
	/* store the data 
	vsrv->vaddr[vsrv->naddr-1].addr		= ipaddr;
	vsrv->vaddr[vsrv->naddr-1].deletable	= 0;*/
	vsrv->vaddr[0].addr		= ipaddr;
	vsrv->vaddr[0].deletable	= 0;
}

/*==============================================================
 TRUE if the minimal configuration isnt done
==============================================================*/
static int chk_min_cfg( vrrp_rt *vsrv )
{
	/*if( vsrv->naddr == 0 ){
		fprintf(stderr, "provide at least one ip for the virtual server\n");
		return -1;
	}*/
	if( vsrv->vrid == 0 ){
		fprintf(stderr, "the virtual id must be set!\n");
		return -1;
	}
	if( vsrv->vif.ipaddr == 0 ){
		fprintf(stderr, "the interface ipaddr must be set!\n");
		return -1;
	}
	/* make vrrp use the native hwaddr and not the virtual one */
	if( vsrv->no_vmac ){
		memcpy( vrrp_hwaddr, vsrv->vif.hwaddr,sizeof(vsrv->vif.hwaddr));
	}
	return(0);
}

/*==============================================================
 open the socket and join the multicast group.
==============================================================*/
static int open_sock( vrrp_rt *vsrv )
{
	struct	ip_mreq req;
	int	ret;
	/* open the socket */
	vsrv->sockfd = socket( AF_INET, SOCK_RAW, IPPROTO_VRRP );
	if( vsrv->sockfd < 0 ){
		int	err = errno;
		VRRP_LOG(("cant open raw socket. errno=%d. (try to run it as root)\n"
						, err));
		return -1;
	}
	/* join the multicast group */
	memset( &req, 0, sizeof (req));
	req.imr_multiaddr.s_addr = htonl(INADDR_VRRP_GROUP);
	req.imr_interface.s_addr = htonl(vsrv->vif.ipaddr); 
	ret = setsockopt (vsrv->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
			       (char *) &req, sizeof (struct ip_mreq));
	if( ret < 0 ){
		int	err = errno;
		VRRP_LOG(("cant do IP_ADD_MEMBERSHIP errno=%d\n",err));
		return -1;
	}
	return 0;
}

/**************************************************************************
 
***************************************************************/
static char *pidfile_get_name( vrrp_rt *vsrv )
{
	static char pidfile[FILENAME_MAX+1];
	snprintf( pidfile, sizeof(pidfile), "%s/" VRRP_PID_FORMAT
					, PidDir
					, vsrv->vif.ifname 
					, vsrv->vrid );
	return pidfile;
}

/***************************************************************
 
***************************************************************/
static void pidfile_rm( vrrp_rt *vsrv )
{
	unlink( pidfile_get_name(vsrv) );
}

/***************************************************************
 return 0 if there is no valid pid in the pidfile or no pidfile
*********************************************************************************************/
static int pidfile_exist( vrrp_rt *vsrv )
{
	char	*name	= pidfile_get_name(vsrv);
	FILE	*fIn	= fopen( name, "r" );
	pid_t	pid;
	/* if there is no file */
	if( !fIn )		return 0;
	fscanf( fIn, "%d", &pid );
	fclose( fIn );
	/* if there is no process, remove the stale file */
	if( kill( pid, 0 ) ){
		fprintf(stderr, "Remove a stale pid file %s\n", name );
		pidfile_rm( vsrv );
		return 0;
	}
	/* if the kill suceed, return an error */
	return -1;
}


/***************************************************************
 
***************************************************************/
static int pidfile_write( vrrp_rt *vsrv )
{
	char	*name	= pidfile_get_name(vsrv);
	FILE	*fOut	= fopen( name, "w" );
	if( !fOut ){
		fprintf( stderr, "Can't open %s (errno %d %s)\n", name 
						, errno
						, strerror(errno) 
						);
		return -1;
	}
	fprintf( fOut, "%d\n", getpid() );
	fclose( fOut );
	return(0);
}


/*=================================================================

==============================================================
static int ifname_to_idx( char *ifname )
{
	struct ifreq	ifr;
	int		fd	= socket(AF_INET, SOCK_DGRAM, 0);
	int		ifindex = -1;
	if (fd < 0) 	return (-1);
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	if (ioctl(fd, SIOCGIFINDEX, (char *)&ifr) == 0)
		ifindex = ifr.ifr_ifindex;
	close(fd);
	return ifindex;
}*/


/*==============================================================
let virtual IP address be associative with the interface 
=====================================================================
static int ipaddr_ops( vrrp_rt *vsrv, int addF )
{
	int	i, err	= 0;
	int	ifidx	= ifname_to_idx( vsrv->vif.ifname );
	struct in_addr in;

	for( i = 0; i < vsrv->naddr; i++ ){
		vip_addr	*vadd = &vsrv->vaddr[i];
		if( !addF && !vadd->deletable ) 	continue;

		if( ipaddr_op( ifidx , vadd->addr, addF)){
			err = 1;
			vadd->deletable = 0;
			in.s_addr = htonl(vadd->addr);
			VRRP_LOG(("cant %s the address %s to %s\n"
						, addF ? "set" : "remove"
						, inet_ntoa(in)
						, vsrv->vif.ifname));
		}else{
			vadd->deletable = 1;
		}
	}
	return err;
}*/


/*=================================================================

==============================================================*/
static int vrrp_dlt_len( vrrp_rt *rt )
{
	return ETHER_HDR_LEN;	/* hardcoded for ethernet */
}

/*==============================================================

==============================================================*/
static int vrrp_iphdr_len( vrrp_rt *vsrv )
{
	return sizeof( struct iphdr );
}

/*==============================================================

==============================================================*/
static int vrrp_hd_len( vrrp_rt *vsrv )
{
	return sizeof( vrrp_pkt ) + sizeof(uint32_t)
						+ VRRP_AUTH_LEN;
	/*return sizeof( vrrp_pkt ) + vsrv->naddr*sizeof(uint32_t)
						+ VRRP_AUTH_LEN;*/
}


/*==============================================================

==============================================================*/
static void vrrp_build_dlt( vrrp_rt *vsrv, char *buffer, int buflen )
{
	/* hardcoded for ethernet */
	struct ether_header *	eth = (struct ether_header *)buffer;
	/* destination address --rfc1122.6.4*/
	eth->ether_dhost[0]	= 0x01;
	eth->ether_dhost[1]	= 0x00;
	eth->ether_dhost[2]	= 0x5E;
	eth->ether_dhost[3]	= (INADDR_VRRP_GROUP >> 16) & 0x7F;
	eth->ether_dhost[4]	= (INADDR_VRRP_GROUP >>  8) & 0xFF;
	eth->ether_dhost[5]	=  INADDR_VRRP_GROUP        & 0xFF;
	/* source address --rfc2338.7.3 */
	memcpy( eth->ether_shost, vrrp_hwaddr, sizeof(vrrp_hwaddr)); 
	/* type */
	eth->ether_type		= htons( ETHERTYPE_IP );
}


/*==============================================================

==============================================================*/
static u_short in_csum( u_short *addr, int len, u_short csum)
{
	register int nleft = len;
	const u_short *w = addr;
	register u_short answer;
	register int sum = csum;

	/*
	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
	 *  we add sequential 16 bit words to it, and at the end, fold
	 *  back all the carry bits from the top 16 bits into the lower
	 *  16 bits.
	 */
	while (nleft > 1)  {
		sum += *w++;
		nleft -= 2;
	}

	/* mop up an odd byte, if necessary */
	if (nleft == 1)
		sum += htons(*(u_char *)w << 8);

	/*
	 * add back carry outs from top 16 bits to low 16 bits
	 */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */
	return (answer);
}


/*==============================================================

==============================================================*/
static void vrrp_build_ip( vrrp_rt *vsrv, char *buffer, int buflen )
{
	struct iphdr * ip = (struct iphdr *)(buffer);
	ip->ihl		= 5;
	ip->version	= 4;	
	ip->tos		= 0;
	ip->tot_len	= ip->ihl*4 + vrrp_hd_len( vsrv );
	ip->tot_len	= htons(ip->tot_len);
	ip->id		= ++ip_id;
	ip->frag_off	= 0;
	ip->ttl		= VRRP_IP_TTL;
	ip->protocol	= IPPROTO_VRRP;
	ip->saddr	= htonl(vsrv->vif.ipaddr);
	ip->daddr	= htonl(INADDR_VRRP_GROUP);
	/* checksum must be done last */
	ip->check	= in_csum( (u_short*)ip, ip->ihl*4, 0 );
}

/*==============================================================

==============================================================*/
static int vrrp_build_vrrp( vrrp_rt *vsrv, int prio, char *buffer, int buflen )
{
	//int	i;
	vrrp_if	 *vif	= &vsrv->vif;
	vrrp_pkt *hd	= (vrrp_pkt *)buffer;
	uint32_t *iparr	= (uint32_t *)((char *)hd+sizeof(*hd));
	
	hd->vers_type	= (VRRP_VERSION<<4) | VRRP_PKT_ADVERT;
	hd->vrid	= vsrv->vrid;
	hd->priority	= prio;
	hd->naddr	= vsrv->naddr;
	hd->auth_type	= vsrv->vif.auth_type;
	hd->adver_int	= vsrv->adver_int/VRRP_TIMER_HZ;
	/* copy the ip addresses */
	//for( i = 0; i < vsrv->naddr; i++ ){
		iparr[0] = htonl( vsrv->vaddr[0].addr );
	//}
	hd->chksum	= in_csum( (u_short*)hd, vrrp_hd_len(vsrv), 0);
	/* copy the passwd if the authentication is VRRP_AH_PASS */
	if( vif->auth_type == VRRP_AUTH_PASS ){
		//char	*pw	= (char *)hd+sizeof(*hd)+vsrv->naddr*4;
		char	*pw	= (char *)hd+sizeof(*hd)+4;
		memcpy( pw, vif->auth_data, sizeof(vif->auth_data));
	}
	return(0);
}


/*==============================================================

==============================================================*/
static void vrrp_build_pkt( vrrp_rt *vsrv, int prio, char *buffer, int buflen )
{
//	printf("dltlen=%d iplen=%d", vrrp_dlt_len(vsrv), vrrp_iphdr_len(vsrv) );
	/* build the ethernet header */
	vrrp_build_dlt( vsrv, buffer, buflen );
	buffer += vrrp_dlt_len(vsrv);
	buflen -= vrrp_dlt_len(vsrv);
	/* build the ip header */
	vrrp_build_ip( vsrv, buffer, buflen );
	buffer += vrrp_iphdr_len(vsrv);
	buflen -= vrrp_iphdr_len(vsrv);
	/* build the vrrp header */
	vrrp_build_vrrp( vsrv, prio, buffer, buflen );
}

/*==============================================================

==============================================================*/
static int vrrp_send_pkt( vrrp_rt *vsrv, char *buffer, int buflen )
{
	struct sockaddr from;
	int	len;
	int	fd = socket(PF_PACKET, SOCK_PACKET, 0x300); /* 0x300 is magic */
// WORK:
	if( fd < 0 ){
		perror( "socket" );
		return -1;
	}
	/* build the address */
	memset( &from, 0 , sizeof(from));
	strcpy( from.sa_data, vsrv->vif.ifname );
	/* send the data */
	len = sendto( fd, buffer, buflen, 0, &from, sizeof(from) ); 
	/* sendto prototype:
	ssize_t sendto(int socket, const void *message, size_t length,
       int flags, const struct sockaddr *dest_addr, socklen_t dest_len);
	*/
//printf("len=%d\n",len);
	close( fd );
	return len;
}

/*==============================================================

======================================================================*/
static int vrrp_send_adv( vrrp_rt *vsrv, int prio )
{
	int	buflen, ret;
	char *	buffer;
#if 0	/* just for debug */
	struct in_addr in;
	in.s_addr = htonl(vsrv->vif.ipaddr);
	printf("send an advertissement on %s\n",inet_ntoa(in) );
#endif
	/* alloc the memory */
	buflen = vrrp_dlt_len(vsrv) + vrrp_iphdr_len(vsrv) + vrrp_hd_len(vsrv);
	buffer = calloc( buflen, 1 );
	assert( buffer );
	/* build the packet  */
	vrrp_build_pkt( vsrv, prio, buffer, buflen );
	/* send it */
	if (!failure)
		ret = vrrp_send_pkt( vsrv, buffer, buflen );
	/* build the memory */
	free( buffer );
	return ret;
}


/***************************************************************

**************************************************************
static int send_gratuitous_arp( vrrp_rt *vsrv, int addr, int vAddrF )
{
	struct m_arphdr
	{
    unsigned short int ar_hrd;          // Format of hardware address.  
    unsigned short int ar_pro;          // Format of protocol address. 
    unsigned char ar_hln;               // Length of hardware address.  
    unsigned char ar_pln;               // Length of protocol address.  
    unsigned short int ar_op;           // ARP opcode (command).  
    // Ethernet looks like this : This bit is variable sized however...  
    unsigned char __ar_sha[ETH_ALEN];   // Sender hardware address.  
    unsigned char __ar_sip[4];          // Sender IP address.  
    unsigned char __ar_tha[ETH_ALEN];   // Target hardware address.  
    unsigned char __ar_tip[4];          // Target IP address.  
	};
	char	buf[sizeof(struct m_arphdr)+ETHER_HDR_LEN];
	char	buflen	= sizeof(struct m_arphdr)+ETHER_HDR_LEN;
	struct ether_header 	*eth	= (struct ether_header *)buf;
	struct m_arphdr	*arph = (struct m_arphdr *)(buf+vrrp_dlt_len(vsrv));
	char	*hwaddr	= vAddrF ? vrrp_hwaddr : vsrv->vif.hwaddr;
	int	hwlen	= ETH_ALEN;

	// hardcoded for ethernet 
	memset( eth->ether_dhost, 0xFF, ETH_ALEN );
	memcpy( eth->ether_shost, hwaddr, hwlen );
	eth->ether_type	= htons(ETHERTYPE_ARP);

	// build the arp payload 
	memset( arph, 0, sizeof( *arph ) );
	arph->ar_hrd	= htons(ARPHRD_ETHER);
	arph->ar_pro	= htons(ETHERTYPE_IP);
	arph->ar_hln	= 6;
	arph->ar_pln	= 4;
	arph->ar_op	= htons(ARPOP_REQUEST);
	memcpy( arph->__ar_sha, hwaddr, hwlen );
	addr = htonl(addr);
	memcpy( arph->__ar_sip, &addr, sizeof(addr) );
	memcpy( arph->__ar_tip, &addr, sizeof(addr) );
	return vrrp_send_pkt( vsrv, buf, buflen );
}*/


/*==============================================================

=====================================================================*/
static void state_goto_master( vrrp_rt *vsrv )
{
	//int	i;
	/*vrrp_if	*vif = &vsrv->vif;
	 set the VRRP MAC address -- rfc2338.7.3 
	if( !vsrv->no_vmac ){
		hwaddr_set( vif->ifname, vrrp_hwaddr, sizeof(vrrp_hwaddr) );
		rcvhwaddr_op( vif->ifname, vif->hwaddr, sizeof(vif->hwaddr), 1);
	}*/
	/* add the ip addresses */
	//ipaddr_ops( vsrv, 1 );

	/* send an advertisement */
	vrrp_send_adv( vsrv, vsrv->priority );
	/* send gratuitous arp for each virtual ip */
	/*
	for( i = 0; i < vsrv->naddr; i++ )
		send_gratuitous_arp( vsrv, vsrv->vaddr[i].addr, 1 );
	*/
	/* init the struct */
	VRRP_TIMER_SET( vsrv->adver_timer, vsrv->adver_int );
	vsrv->state = VRRP_STATE_MAST;
}

/*==============================================================

=====================================================================================*/
static void state_init( vrrp_rt *vsrv )
{
	if( vsrv->priority == VRRP_PRIO_OWNER 
			|| vsrv->wantstate == VRRP_STATE_MAST ){
		state_goto_master( vsrv );
	} else {
		// RFC 5798 here: Set Master_Adver_Interval to Advertisement_Interval ?
		int delay = 3*vsrv->adver_int;// + VRRP_TIMER_SKEW(vsrv); //Master_Down_Interval 
		VRRP_TIMER_SET( vsrv->ms_down_timer, delay );
		vsrv->state = VRRP_STATE_BACK;
	}
}


/*==============================================================
check incoming packets
==============================================================*/
static int vrrp_in_chk( vrrp_rt *vsrv, struct iphdr *ip )
{
	int		ihl = ip->ihl << 2;
	vrrp_pkt *	hd = (vrrp_pkt *)((char *)ip + ihl);
	vrrp_if 	*vif	= &vsrv->vif;
	/* MUST verify that the IP TTL is 255 */
	if( ip->ttl != VRRP_IP_TTL ) {
		VRRP_LOG(("invalid ttl. %d and expect %d", ip->ttl,VRRP_IP_TTL));
		return 1;
	}
	/* MUST verify the VRRP version */
	if( (hd->vers_type >> 4) != VRRP_VERSION ){
		VRRP_LOG(("invalid version. %d and expect %d"
			, (hd->vers_type >> 4), VRRP_VERSION));
		return 1;
	}
	/* MUST verify that the received packet length is greater than or
	** equal to the VRRP header */
	if( (ntohs(ip->tot_len)-ihl) <= sizeof(vrrp_pkt) ){
		VRRP_LOG(("ip payload too short. %d and expect at least %d"
			, ntohs(ip->tot_len)-ihl, sizeof(vrrp_pkt) ));
		return 1;
	}
	/* WORK: MUST verify the VRRP checksum */
	if( in_csum( (u_short*)hd, vrrp_hd_len(vsrv), 0) ){
		VRRP_LOG(("Invalid vrrp checksum" ));
		return 1;
	}
/* MUST perform authentication specified by Auth Type */
 	/* check the authentication type */
	if( vif->auth_type != hd->auth_type ){		
		VRRP_LOG(("receive a %d auth, expecting %d!", vif->auth_type
							, hd->auth_type));
		return 1;
	}
	/* check the authentication if it is a passwd */
	if( hd->auth_type != VRRP_AUTH_PASS ){
		char	*pw	= (char *)ip + ntohs(ip->tot_len)
				  -sizeof(vif->auth_data);
		if( memcmp( pw, vif->auth_data, sizeof(vif->auth_data)) ){
			VRRP_LOG(("receive an invalid passwd!"));
			return 1;
		}
	}

	/* MUST verify that the VRID is valid on the receiving interface */
	if( vsrv->vrid != hd->vrid ){
		return 1;
	}

	/* MAY verify that the IP address(es) associated with the VRID are
	** valid */
	/* WORK: currently we don't */

	/* MUST verify that the Adver Interval in the packet is the same as
	** the locally configured for this virtual router */
	if( vsrv->adver_int/VRRP_TIMER_HZ != hd->adver_int ){
		VRRP_LOG(("advertissement interval mismatch mine=%d rcved=%d"
			, vsrv->adver_int, hd->adver_int ));
		return 1;
	}

	return 0;
}


/***************************************************************

***************************************************************/
static int vrrp_read( vrrp_rt *vsrv, char *buf, int buflen )
{
	fd_set		readfds;
	struct timeval	timeout;
	uint32_t	next	= 0xFFFFFFFF;
	int		len	= 0;
	/* cpu the next timer */
	if( VRRP_TIMER_IS_RUNNING( vsrv->adver_timer ) ){
		int32_t	delta = VRRP_TIMER_DELTA(vsrv->adver_timer);
		if( delta < 0 )	delta = 0;
		next = VRRP_MIN( next, delta );
	}else{	/* here vsrv->ms_down_timer is assumed running */
		int32_t	delta = VRRP_TIMER_DELTA(vsrv->ms_down_timer);
		assert( VRRP_TIMER_IS_RUNNING( vsrv->ms_down_timer ) );
		if( delta < 0 )	delta = 0;
		next = VRRP_MIN( next, delta );
	}
	/* setup the select() */
	FD_ZERO( &readfds );
	FD_SET( vsrv->sockfd, &readfds );
	timeout.tv_sec	= next / VRRP_TIMER_HZ;
	timeout.tv_usec = next % VRRP_TIMER_HZ;
//printf( "val %u,%u %u\n", timeout.tv_sec, timeout.tv_usec, next );
	if( select( vsrv->sockfd + 1, &readfds, NULL, NULL, &timeout ) > 0 ){
		len = read( vsrv->sockfd, buf, buflen );
//		printf("packet received (%d bytes)\n",len);
		if( vrrp_in_chk( vsrv, (struct iphdr *)buf ) ){
			printf("bogus packet!\n");
			len = 0;
		}
	}
	return len;	
}


/*==============================================================
 
==============================================================*/
static void state_back( vrrp_rt *vsrv )
{
	char		buf[300];	/* WORK: lame ! */
	int		len	= vrrp_read( vsrv, buf, sizeof(buf) );
	struct iphdr	*iph	= (struct iphdr *)buf;
	vrrp_pkt	*hd	= (vrrp_pkt *)((char *)iph + (iph->ihl<<2));
	if( (!len && VRRP_TIMER_EXPIRED(vsrv->ms_down_timer)) 
			|| vsrv->wantstate == VRRP_STATE_MAST ){
		state_goto_master( vsrv );
		return;
	}
	if( !len )	return;
	if ( hd->priority == 0 ) {
		VRRP_TIMER_SET( vsrv->ms_down_timer, 0);//VRRP_TIMER_SKEW(vsrv) );
		state_goto_master( vsrv );
	} else if( !vsrv->preempt || hd->priority >= vsrv->priority ) {
		int delay = 3*vsrv->adver_int;// + VRRP_TIMER_SKEW(vsrv); //Master_Down_Interval 
		VRRP_TIMER_SET( vsrv->ms_down_timer, delay );
	}
}


/*==============================================================

==============================================================*/
static void state_leave_master( vrrp_rt *vsrv, int advF )
{
	//uint32_t	addr[1024];
	//vrrp_if		*vif = &vsrv->vif;
	/* restore the original MAC addresses 
	if( !vsrv->no_vmac ){
		hwaddr_set( vif->ifname, vif->hwaddr, sizeof(vif->hwaddr) );
		rcvhwaddr_op( vif->ifname, vif->hwaddr, sizeof(vif->hwaddr), 0);
	}*/
	/* remove the ip addresses */
	//ipaddr_ops( vsrv, 0 );

	/* if we stop vrrpd, warn the other routers to speed up the recovery */
	if( advF ){
		vrrp_send_adv( vsrv, VRRP_PRIO_STOP );
	}

	/* send gratuitous ARP for all the non-vrrp ip addresses to update
	** the cache of remote hosts using these addresses 
	if( !vsrv->no_vmac ){
		int		i, naddr;
		naddr = ipaddr_list( ifname_to_idx(vif->ifname), addr
				, sizeof(addr)/sizeof(addr[0]) );
		for( i = 0; i < naddr; i++ )
			send_gratuitous_arp( vsrv, addr[i], 0 );
	}*/
}


/*==============================================================
 
==============================================================*/
static void state_mast( vrrp_rt *vsrv )
{
	char		buf[300];	/* WORK: lame ! */
	int		len	= vrrp_read( vsrv, buf, sizeof(buf) );
	struct iphdr	*iph	= (struct iphdr *)buf;
	vrrp_pkt	*hd	= (vrrp_pkt *)((char *)iph + (iph->ihl<<2));
	if( vsrv->wantstate == VRRP_STATE_BACK ){
		goto be_backup;
	}
	if( !len && VRRP_TIMER_EXPIRED(vsrv->adver_timer) ){
		vrrp_send_adv( vsrv, vsrv->priority );
		VRRP_TIMER_SET(vsrv->adver_timer,vsrv->adver_int);
		return;
	}
	if( !len )	return;
	if( hd->priority == 0 ){
		vrrp_send_adv( vsrv, vsrv->priority );
		VRRP_TIMER_SET(vsrv->adver_timer,vsrv->adver_int);
	}else if( hd->priority > vsrv->priority ||
			(hd->priority == vsrv->priority &&
			ntohl(iph->saddr) > vsrv->vif.ipaddr) ){
		int delay	= 3*vsrv->adver_int;// + VRRP_TIMER_SKEW(vsrv); //Master_Down_Interval
be_backup:
		VRRP_TIMER_SET( vsrv->ms_down_timer, delay );
		VRRP_TIMER_CLR( vsrv->adver_timer );
		state_leave_master( vsrv, 0 );
		vsrv->state	= VRRP_STATE_BACK;
	}
}


/***************************************************************

***************************************************************/
static void signal_end( int nosig )
{
	vrrp_rt	*vsrv = &glob_vsrv;

	/* remove the pid file */
	pidfile_rm( vsrv );
	/* if the deamon is master, leave this state */
	if( vsrv->state == VRRP_STATE_MAST ){
		state_leave_master( vsrv, 1 );
	}
	exit( 0 );
}

/***************************************************************

***************************************************************/
static void signal_user( int nosig )
{
	vrrp_rt	*vsrv = &glob_vsrv;

	if( nosig == SIGUSR1 ){
		vsrv->wantstate = VRRP_STATE_MAST;
	}
	if( nosig == SIGUSR2 ){
		vsrv->wantstate = VRRP_STATE_BACK;
	}
	
	/* rearm the signal */
	signal( nosig, signal_user );
}


int ufd;
int sock;
struct sockaddr_in s_sa;
struct sockaddr_in r_sa;
//void signal_handler_IO (int status);   /* definition of signal handler */
int wait_flag=1; 
struct	ip_mreq r_ma;
int mSock;


void *recv_thread(void);
void *send_thread(void);
/*==============================================================
 signal handler. set wait_flag to 0 to indicate data have been received.
==============================================================*/
void signal_handler_IO (int status)                                          
{                                                                            
	//printf("received SIGIO signal: %d.\n", status);
    wait_flag = 0;                                                         
}

int rate1;
int rate2;
int fCount;
/*==================================================================================================
 main function
==================================================================================================*/
int main( int argc, char *argv[] )
{
	//rate = atoi(argv[argc]);
	//printf("rate:%d argc:%s\n", rate, argv[argc]);
	fCount = atoi(argv[argc-3]);
	rate1 = atoi(argv[argc-2]);
	rate2 = atoi(argv[argc-1]);
	
//	printf("rate1=%d , argc-2: %d\n", rate1, argv[argc-2]);
//	printf("rate2=%d , argc-1: %d\n", rate2, argv[argc-1]);
	printf("rate=%d/%d \n", rate2, rate1);


	vrrp_rt	*vsrv = &glob_vsrv;

#if 1	/* for debug only */
	setbuf(stdout,NULL);
	setbuf(stderr,NULL);
#endif
	
	snprintf( PidDir, sizeof(PidDir), "%s", VRRP_PIDDIR_DFL );

	//init virtual srv
	memset( vsrv, 0, sizeof(*vsrv) );
	vsrv->state	= VRRP_STATE_INIT;
	vsrv->priority	= VRRP_PRIO_DFL;
	vsrv->adver_int	= VRRP_ADVER_DFL*VRRP_TIMER_HZ;
	vsrv->preempt	= VRRP_PREEMPT_DFL;
	vsrv->no_vmac	= 1; //GroupAddress uses no virtual MAC

	//vif->ipaddr

	/* parse the command line */
	argc = parse_cmdline(vsrv,argc, argv );
	if( argc < 0 ) {
		return -1;
	}

	/* add the virtual server ip 
	for( ; argv[argc]; argc++ ){
		uint32_t ipaddr = inet_addr( argv[argc] );
		cfg_add_ipaddr( vsrv, ntohl(ipaddr) );
	}*/
	vsrv->naddr=0;
	uint32_t ipaddr=0;
	cfg_add_ipaddr( vsrv, ntohl(ipaddr) );

	/* check if the minimal configuration has been done */
	if( chk_min_cfg( vsrv ) ){
		fprintf(stderr, "try '%s -h' to read the help\n", argv[0]);
		return -1;
	}
	if( open_sock( vsrv ) ){
		return -1;
	}

	/* the init is completed */
	vsrv->initF = 1;

	/* init signal handler */
	signal( SIGINT, signal_end );
	signal( SIGTERM, signal_end );
	signal( SIGUSR1, signal_user );
	signal( SIGUSR2, signal_user );

	/* try to write a pid file */
	if( pidfile_exist( vsrv ) )	return -1;
	pidfile_write( vsrv );

	////////////////////////////read data from UART/////////////////////////////
	struct termios oldtio, newtio;
	struct sigaction saio;           /* definition of signal action */
	sigset_t sigset;

	//printf("Start...\n");

	/* �}��PC��COM2�q�T��
        O_RDWR:  �iŪ�g���äD���}����
        O_NOCTTY:�i�Dlinux�A�o�ӵ{�����Q����tty�ɭ��A�p�G���]�o�ӺX��
                 ���򦳨ǿ��J(�p���Labort�H��)�|�v�T���{���C
	*/
	ufd=open(MODEMDEVICE, O_RDWR | O_NOCTTY | O_NONBLOCK);
	if(ufd<0)
	{
	   perror(MODEMDEVICE);
	   exit(1);
	}
	//printf("Open...\n");

	/* install the signal handler before making the device asynchronous */
    saio.sa_handler = signal_handler_IO;
	sigemptyset(&sigset);

	saio.sa_mask = sigset;
    saio.sa_flags = 0;                                                        
    saio.sa_restorer = NULL;                                                 
    sigaction(SIGIO,&saio,NULL);               

	/* allow the process to receive SIGIO */                                  
    fcntl(ufd, F_SETOWN, getpid());                                            
	/* Make the file descriptor asynchronous (the manual page says only       
       O_APPEND and O_NONBLOCK, will work with F_SETFL...) */                 
    fcntl(ufd, F_SETFL, FASYNC); 

	tcgetattr(ufd,&oldtio);        //�N�ثe�׺ݾ��ѼƦs��oldtio���c

	bzero(&newtio,sizeof(newtio));//�M��newtio���c�A���s�]�w�q�T���w

	/* �q�T���w�]��8N1 */
	newtio.c_cflag = BAUDRATE|CS8|CLOCAL|CREAD;
	newtio.c_iflag = IGNPAR;
	newtio.c_oflag = 0;
	newtio.c_lflag = 0;//ICANON; //�]�����W�Ҧ�
	newtio.c_cc[VMIN]=5;
    newtio.c_cc[VTIME]=1;

	tcflush(ufd,TCIFLUSH);
	/* �s��termios���c�@���q�T�𪺰Ѽ� */
	tcsetattr(ufd,TCSANOW,&newtio);

	//printf("Reading....\n");

	/////////////////////////////////socket/////////////////////////////////

	sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (-1 == sock) /* if socket failed to initialize, exit */
	{
		printf("Error Creating Socket");
		exit(EXIT_FAILURE);
	}

	bzero(&s_sa, sizeof(s_sa));
	s_sa.sin_family = AF_INET;
	s_sa.sin_port = htons(17654);
	inet_pton(AF_INET, argv[9], &s_sa.sin_addr);

//MULTICAST
	int	mRet;
	mSock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
	if( mSock < 0 ){
		printf("cant open raw socket. errno=%d. (try to run it as root)\n", errno);
		return -1;
	}
	
	int reuse = 1;
    if (setsockopt(mSock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
        fprintf(stderr, "setsockopt: %d\n", errno);
        return 1;
    }

	bzero(&r_sa, sizeof(r_sa));
	r_sa.sin_family = AF_INET;
	r_sa.sin_port = htons(17654);
	r_sa.sin_addr.s_addr = htonl(INADDR_ANY);
	//inet_pton(AF_INET, "239.255.255.1", &r_sa.sin_addr);

	if (-1 == bind(mSock,(struct sockaddr *)&r_sa, sizeof(r_sa)))
	{
		perror("error bind failed");
		close(mSock);
		exit(EXIT_FAILURE);
	}
	memset( &r_ma, 0, sizeof (r_ma));
	//	r_ma.imr_multiaddr.s_addr = htonl(0xe0000101);
	r_ma.imr_multiaddr.s_addr = inet_addr("224.0.0.200");
	r_ma.imr_interface.s_addr = htonl(vsrv->vif.ipaddr); 

	// join a multicast group
	mRet = setsockopt (mSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &r_ma, sizeof (struct ip_mreq));
	if( mRet < 0 ){
		printf("cant do IP_ADD_MEMBERSHIP errno=%d\n",errno);
		return -1;
	}

	int tRes;
	pthread_t rThread;
	pthread_t sThread;

	tRes = pthread_create(&rThread, NULL, recv_thread, NULL);
	if (tRes != 0){
		perror("Thread creation failed");
		exit(EXIT_FAILURE);
	}
	tRes = pthread_create(&sThread, NULL, send_thread, NULL);
	if (tRes != 0){
		perror("Thread creation failed");
		exit(EXIT_FAILURE);
	}
	/* main loop */
	while( 1 ){	
		switch( vsrv->state ){
		case VRRP_STATE_INIT:	state_init( vsrv );	break;//printf("init\n"); break;
		case VRRP_STATE_BACK:	state_back( vsrv );	break;//printf("backup\n"); break;
		case VRRP_STATE_MAST:	state_mast( vsrv );	break;//printf("master\n");	break;
		}
		//if(uBuf[0]=='/')break;
	}
	close(sock); /* close the socket */
	close(mSock);
	close(ufd);

	/* �^�s�ª��q�T���Ѽ� */
	tcsetattr(ufd,TCSANOW,&oldtio);

	return(0);
}

/*==============================================================
 socket thread
==============================================================*/
void *recv_thread()
{
	vrrp_rt	*vsrv;
	unsigned char sbuffer[256];
	int uart_write, sock_recv;
	socklen_t fromlen;
	int i;

	while(1){
		sock_recv = recvfrom(mSock, (void *)sbuffer, 256, 0, (struct sockaddr *)&r_sa, &fromlen);
		//sock_recv = read(mSock, sbuffer, 256 );
		if (sock_recv < 0)	fprintf(stderr, "%s\n", strerror(errno));
		else{
			printf("Receive from Server: \nlength=%d data= ", sock_recv);
			for (i=0;i<sock_recv;i++) printf("%c", sbuffer[i]);
			printf("\n");
			vsrv = &glob_vsrv;
			if(vsrv->state==VRRP_STATE_MAST){
				uart_write = write(ufd, sbuffer , sock_recv);
				printf("***The active translator has sent data to ZigBee device.\n");
			}
		}
	}
	if (sock_recv < 0)	fprintf(stderr, "%s\n", strerror(errno));
}


/*==============================================================
 socket thread
==============================================================*/
void *send_thread()
{
	vrrp_rt	*vsrv;
	unsigned char uBuf[256];
	int uart_read;
	int i;
	int recv=0;

	srand(time(NULL));

	while(1){
		if(wait_flag==0) {
			recv++;
			if(recv%100==1) failure=0;
			uart_read=read(ufd,uBuf,255);
			uBuf[uart_read]=0;
			
			//printf("Receive from ZigBee device: \nlength=%d data= ", uart_read);
			//for (i=0;i<uart_read;i++) printf("%x ",uBuf[i]);
			//printf("\n");
			//printf("sequence:%d\n", uBuf[uart_read-1]*16+uBuf[uart_read-2]);
			printf("sequence:%d\n", uBuf[uart_read-1]);
			
			if(!failure) {
				if( rand()%rate1 < rate2 ) {
					failure = 1; 
					failCount = fCount;
					printf("fail\n");
				}
				else{
					vsrv = &glob_vsrv;

					if(vsrv->state==VRRP_STATE_MAST) {
						if(sendto(sock, uBuf, uart_read, 0,(struct sockaddr*)&s_sa, sizeof(s_sa)) <0)
							printf("Error sending packet: %s\n", strerror(errno));
						else printf("***The active translator has sent data to server.\n");
					}
				}
				
			}
			else if (failCount == 0){
				failure=0;
				printf("come back\n");
			}
			else
				failCount--;
			if(recv==1000) exit(1);
			wait_flag = 1;
		}
	}
}