#include <stdio.h>   
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <net/if.h>

#include <sendpkt-inl.h>

static volatile uint64_t g_rand_seed = 0x013579ABCDEF;

#define SENDPACKET_CKSUM_CARRY(x)   (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff))

unsigned int deliver_rand(void)
{
	return g_rand_seed ^ (unsigned int)random();
}

unsigned int deliver_rand_range(unsigned int start, unsigned int end)
{
	unsigned int rand_num = deliver_rand();

	if(start > end)
	{
		return end + rand_num % (start - end + 1);
	}

	return start + rand_num % (end - start + 1);
}



/* ascii字符转16进制 */
char MESA_ascii_to_hex(char ascii)
{
	char c = 0;
	
	switch(ascii)
	{
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			c = ascii - 0x30;
		break;

		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
			c = 10 + ascii - 0x61;
		break;

		case 'A':
		case 'B':
		case 'C':
		case 'D':
		case 'E':
		case 'F':
			c = 10 + ascii - 0x41;
		break;
	}

	return c;
}



/* 2012-04-10 LiJia add, 获取网卡MAC地址
参数:
	device: 网卡名称
	mac: 存储MAC地址的数组,结果为网络序,
		 如网卡MAC地址为11:22:33:44:55:66,则mac[0]为0x11,mac[5]为0x66.
返回值:
	0: 正常
	-1:错误
*/
int deliver_get_dev_mac(const char *device, unsigned char mac[6])
{
    struct ifreq ifr;
    int fd;

    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0)
    {
        return -1;
    }

    memset(ifr.ifr_ifrn.ifrn_name, 0, sizeof(ifr.ifr_ifrn.ifrn_name));
    strncpy(ifr.ifr_ifrn.ifrn_name, device, sizeof(ifr.ifr_ifrn.ifrn_name));
    if(ioctl(fd, SIOCGIFHWADDR, &ifr) == -1) 
    {
        printf("Cann't get hwaddr of %s:%s\n", device, strerror(errno));
        goto err_exit;
    }

    if(ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER)
    {
		printf("'%s' is not ethernet interface!\n", device);
		goto err_exit;
    }

    memcpy(mac, ifr.ifr_ifru.ifru_addr.sa_data, 6);

    close(fd);

    return 0;

err_exit:
    close(fd);
    return -1;
}



/*  2012-04-11 LiJia add,将MAC字符串形式转换为16进制MAC地址.
参数:
	str:   MAC地址字符串
	delim: 字符串分隔符，常见为':', '-'等,如: xx:xx:xx:xx:xx:xx
			如果字符串无分隔符，delim设为-1.
	mac:   存储MAC地址的数组(指针),结果为网络序,
		   如网卡MAC地址为11:22:33:44:55:66,则mac[0]为0x11,mac[5]为0x66.
返回值:
	0: 正常
	-1:错误
*/
int deliver_mac_pton(const char *str, int delim, char *mac)
{
#define MAC_STR_LEN_DELIM		(17) /* length of "11:22:33:44:55:66" */
#define MAC_STR_LEN_NODELIM		(12) /* length of "112233445566" */
	const char *s = str;
	int i; 

	/* 检查输入合法性 */
	if(delim != -1)
	{
		if(strlen(str) != MAC_STR_LEN_DELIM) 
		{
			printf("MAC string length error!\n");
			return -1;
		}
	}
	else
	{
		if(strlen(str) != MAC_STR_LEN_NODELIM) 
		{
			printf("MAC string length error!\n");
			return -1;
		}
	}

	/* 检查输入合法性，同时转换成16进制值 */
	for(i = 0; i < 6; i++)
	{
		mac[i] = 0; /* 先清零，赋值语句都是或操作 */
		if(isxdigit(*s)==0)
		{
			printf("MAC string type error!\n");
			return -1;
		}
		mac[i] |= MESA_ascii_to_hex(*s) << 4;
		s++;
		
		if(isxdigit(*s)==0)
		{
			printf("MAC string type error!\n");
			return -1;
		}
		mac[i] |= MESA_ascii_to_hex(*s);
		s++;
		
		if((delim != -1) && i<5 && (*s++ != (char)delim))
		{
			printf("MAC string type error!\n");
			return -1;
		}
	}

	return 0;
}





int sendpacket_in_cksum(unsigned short *addr, int len)
{
    int sum;
    int nleft;
    unsigned short ans;
    unsigned short *w;

    sum = 0;
    ans = 0;
    nleft = len;
    w = addr;

    while (nleft > 1)
    {
        sum += *w++;
        nleft -= 2;
    }
    if (nleft == 1)
    {
        *(char *)(&ans) = *(char *)w;
        sum += ans;
    }
    return (sum);
}



int deliver_do_checksum(unsigned char *buf, int protocol, int len)
{
    struct mesa_ip4_hdr *iph_p;
    struct mesa_ip6_hdr *ip6h_p;
    int ip_hl;
    int sum;
    int is_ipv6 = 0;

    sum = 0;
    iph_p = (struct mesa_ip4_hdr *)buf;

    if(4 == iph_p->ip_v)  /* IP版本号字段，IPv4和IPv6格式是相同的 */
    {
        ip_hl = iph_p->ip_hl << 2;
        ip6h_p = NULL;
    }
    else if(6 == iph_p->ip_v)
    {
        ip6h_p = (struct mesa_ip6_hdr *)buf;
        iph_p = NULL;
        ip_hl = sizeof(struct mesa_ip6_hdr);
        is_ipv6 = 1;
    }
    else
    {
        return (-1);
    }

    switch (protocol)
    {
        case IPPROTO_TCP:
        {
            struct mesa_tcp_hdr *tcph_p = (struct mesa_tcp_hdr *)(buf + ip_hl);

            tcph_p->th_sum = 0;
            if(is_ipv6)
            {
                sum = sendpacket_in_cksum((unsigned short*)&ip6h_p->ip6_src, 32);
            }
            else
            {
                sum = sendpacket_in_cksum((unsigned short *)&iph_p->ip_src, 8);
            }    
            sum += ntohs(IPPROTO_TCP + len);
            sum += sendpacket_in_cksum((unsigned short *)tcph_p, len);
            tcph_p->th_sum = SENDPACKET_CKSUM_CARRY(sum);
            break;
        }
        
        case IPPROTO_UDP:
        {
            struct mesa_udp_hdr *udph_p =(struct mesa_udp_hdr *)(buf + ip_hl);

            udph_p->uh_sum = 0;
            if(is_ipv6)
            {
                sum = sendpacket_in_cksum((unsigned short*)&ip6h_p->ip6_src, 32);
            }
            else
            {
                sum = sendpacket_in_cksum((unsigned short*)&iph_p->ip_src, 8);
            }   
            sum += ntohs(IPPROTO_UDP + len);
            sum += sendpacket_in_cksum((unsigned short*)udph_p, len);
            udph_p->uh_sum = SENDPACKET_CKSUM_CARRY(sum);
            break;
        }
        
        case IPPROTO_IP: 
        {
            iph_p->ip_sum = 0;
            sum = sendpacket_in_cksum((unsigned short*)iph_p, len);
            iph_p->ip_sum = SENDPACKET_CKSUM_CARRY(sum);
            break;
        }
        
        default:
        {
            return (-1);
        }
    }
    return (1);
}




/*
	if playload2 is not NULL, it means that there is ip spice, it must be copied first, then 
	playload is copied
	otherwise, only payload is copied and it includes ip header
*/
int deliver_build_ethhdr(unsigned char *dst, unsigned char *src, unsigned short type,unsigned char *buf)
{
	struct mesa_ethernet_hdr eth_hdr;

	if (!buf)
	{
	    return (-1);
	}

	memcpy(eth_hdr.ether_dhost, dst, ETHER_ADDR_LEN);  /* destination address */
	memcpy(eth_hdr.ether_shost, src, ETHER_ADDR_LEN);  /* source address */
	eth_hdr.ether_type = htons(type);                  /* packet type */

	memcpy(buf, &eth_hdr, sizeof(eth_hdr));
	return (0);
}



/*
	if playload2 is not NULL, it means that there is ip spice, it must be copied first, then 
	playload is copied
	otherwise, only payload is copied and it includes ip header
*/
int deliver_build_ethernet(unsigned char *dst, unsigned char *src, unsigned short type,
            				const unsigned char *payload, int payload_s, unsigned char *buf)
{
	struct mesa_ethernet_hdr eth_hdr;

	if (!buf)
	{
	    return (-1);
	}

	memcpy(eth_hdr.ether_dhost, dst, ETHER_ADDR_LEN);  /* destination address */
	memcpy(eth_hdr.ether_shost, src, ETHER_ADDR_LEN);  /* source address */
	eth_hdr.ether_type = htons(type);                  /* packet type */

	if (payload && payload_s)
	{
	    memcpy(buf + SENDPACKET_ETH_H, payload, payload_s);
	}
	memcpy(buf, &eth_hdr, sizeof(eth_hdr));
	return (0);
}



int deliver_build_ipv6(unsigned char traffic_class, unsigned int flow_lable,
                              unsigned short len, unsigned char next_header, unsigned char hop,
                              const struct in6_addr *src, const struct in6_addr *dst,
                              const unsigned char *payload, int payload_s, unsigned char *buf)
{
	struct mesa_ip6_hdr *ip6_h;

	if(!buf){
		return -1;
	}

	ip6_h = (struct mesa_ip6_hdr *)buf;

	memset(ip6_h, 0, sizeof(struct mesa_ip6_hdr));

	ip6_h->ip6_flags[0]      = 0x60 | ((traffic_class & 0xF0) >> 4);
	ip6_h->ip6_flags[1]      = ((traffic_class & 0x0F) << 4) | ((flow_lable & 0xF0000) >> 16);
	ip6_h->ip6_flags[2]      = flow_lable & 0x0FF00 >> 8;
	ip6_h->ip6_flags[3]      = flow_lable & 0x000FF;
	ip6_h->ip6_payload_len   = htons(len);
	ip6_h->ip6_nxt_hdr       = next_header;
	ip6_h->ip6_hop           = hop;
	memcpy(&ip6_h->ip6_src, src, sizeof(struct in6_addr));
	memcpy(&ip6_h->ip6_dst, dst, sizeof(struct in6_addr));

	if(payload && payload_s)
	{
		memcpy(buf + sizeof(struct mesa_ip6_hdr), payload, payload_s);
	}

	return 0;
}



int deliver_build_ipv4(unsigned short carry_layer_len, unsigned char tos, unsigned short id, 
		unsigned short frag, unsigned char ttl, unsigned char prot, unsigned int src, unsigned int dst, 
		const unsigned char *payload,int payload_s, unsigned char *buf)
{
	struct mesa_ip4_hdr *ip_hdr;

	if (!buf)
	{
	    return (-1);
	}

	ip_hdr = (struct mesa_ip4_hdr *)buf;

	ip_hdr->ip_v    = 4;                             /* version 4 */
	ip_hdr->ip_hl   = 5;                             /* 20 byte header */
	ip_hdr->ip_tos  = tos;                           /* IP tos */
	ip_hdr->ip_len  = htons(SENDPACKET_IP_H + carry_layer_len);      /* total length */
	ip_hdr->ip_id   = htons(id);                     /* IP ID */
	ip_hdr->ip_off  = htons(frag);                   /* fragmentation flags */
	ip_hdr->ip_ttl  = ttl;                           /* time to live */
	ip_hdr->ip_p    = prot;                          /* transport protocol */
	ip_hdr->ip_sum  = 0;                             /* do this later */
	ip_hdr->ip_src.s_addr = src; /* 为什么地址用网络序? 历史遗留原因, 改动太多,只能这么继续了 */
	ip_hdr->ip_dst.s_addr = dst; /* 为什么地址用网络序? 历史遗留原因, 改动太多,只能这么继续了 */
	if (payload && payload_s)
	{
		memcpy(buf + SENDPACKET_IP_H, payload, payload_s);
	}
	
	return (0);
}


int deliver_build_tcp(unsigned short sp, unsigned short dp, unsigned int seq, unsigned int ack, 
						unsigned char th_flags, unsigned short win, unsigned short urg, 
						const char *payload, int payload_s, unsigned char *buf)
{
	struct mesa_tcp_hdr *tcp_hdr;

	if (!buf)
	{
	    return (-1);
	}

	tcp_hdr = (struct mesa_tcp_hdr *)buf;

	tcp_hdr->th_sport   = htons(sp);    /* source port */
	tcp_hdr->th_dport   = htons(dp);    /* destination port */
	tcp_hdr->th_seq     = htonl(seq);   /* sequence number */
	tcp_hdr->th_ack     = htonl(ack);   /* acknowledgement number */
	tcp_hdr->th_flags   = th_flags;      /* control flags */
	tcp_hdr->th_x2      = 0;            /* UNUSED */
	tcp_hdr->th_off     = 5;            /* 20 byte header */
	tcp_hdr->th_win     = htons(win);   /* window size */
	tcp_hdr->th_sum     = 0;            /* checksum done in userland */ 
	tcp_hdr->th_urp     = urg;          /* urgent pointer */

	if (payload && payload_s)
	{
//	    memcpy(buf + SENDPACKET_TCP_H, payload, payload_s);
		memcpy(buf + sizeof(struct mesa_tcp_hdr), payload, payload_s);
	}
	
	return (0);
	
}




