GRE/VXLANのpcapファイルをlibpcap apiを利用して生成

トンネルプロトコルの検証用に、pcapファイルのパケットにGREヘッダおよびVXLANヘッダを付与するツールを作ってみた。
PCAPファイルの読み書きにはlibpcap apiを使用した。
wiresharkで解析できているので問題ないはず。
チェックサムのとこは適当・・・

---
GREヘッダの付与

```c:pcap2gre.h
#ifndef __PCAP2GRE_H__
#define __PCAP2GRE_H__

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <pthread.h>
#include <assert.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <inttypes.h>
#define __STDC_FORMAT_MACROS

#define CONF_INT	0
#define CONF_STR	1
#define CONF_SIZE	1024

#define TCPDUMP_MAGIC		0xa1b2c3d4
#define PCAP_VERSION_MAJOR	2
#define PCAP_VERSION_MINOR	4
#define DLT_EN10MB			1				/* Ethernet (10Mb) */
#define PCAP_SNAPLEN		0xffff

struct pd_timeval {
	uint32_t	tv_sec;
	uint32_t	tv_usec;
};

struct pd_pkthdr {
	struct pd_timeval	ts;
	uint32_t			caplen;
	uint32_t			len;
};

typedef struct ether_h {
	struct ether_addr	ether_dhost;
	struct ether_addr	ether_shost;
	uint16_t			ntag;
}ether_t;

union gre_opt_id
{
	uint32_t	vsid:24;	/* Virtual Subnet ID (VSID)			*/
	uint32_t	flowid:8;	/* Flow ID							*/
};

union gre_opt
{
	struct {
		uint32_t	csum:16;	/* Checksum							*/
		uint32_t	pad1:16;
	};

	/* RFC2890 Support */
	uint32_t		key;		/* Key								*/
	uint32_t		seq;		/* Sequence Number					*/

	/* NVGRE Support */
	struct {
		uint32_t	vsid:24;	/* Virtual Subnet ID (VSID)			*/
		uint32_t	flowid:8;	/* Flow ID							*/
	};
};

struct gre
{
	#if __BYTE_ORDER == __LITTLE_ENDIAN
	    uint8_t		pad1:4;		/* Reserved0 (bits 4-7)				*/
		uint8_t		sflg:1;		/* Sequence Number Present (bit 3)	*/
		uint8_t		kflg:1;		/* Key Present (bit 2)				*/
		uint8_t		pad0:1;		/* Padding							*/
	    uint8_t		cflg:1;		/* Checksum Present (bit 0)			*/
		uint8_t		ver:3;		/* Version Number (bits 5-7)		*/
	    uint8_t		pad2:5;		/* Reserved0 (bits 0-4)				*/
	#endif

	#if __BYTE_ORDER == __BIG_ENDIAN
	    uint8_t		cflg:1;		/* Checksum Present (bit 0)			*/
		uint8_t		pad0:1;		/* Padding							*/
		uint8_t		kflg:1;		/* Key Present (bit 2)				*/
		uint8_t		sflg:1;		/* Sequence Number Present (bit 3)	*/
	    uint8_t		pad1:4;		/* Reserved0 (bits 4-7)				*/
	    uint8_t		pad2:5;		/* Reserved0 (bits 0-4)				*/
		uint8_t		ver:3;		/* Version Number (bits 5-7)		*/
	#endif

	uint16_t		proto;		/* Protocol Type (2 octets)			*/
	union gre_opt	opt[3];		/* Optional Fields					*/
};

typedef enum gretype {
	GRE_RFC2784 = 0,
	GRE_RFC2890,
	GRE_NVGRE,
	GRE_MAX
} gretype_e;

struct sys_conf {
	uint32_t			gre_type;
	struct in_addr		src;
	struct in_addr		dst;
	bool				checksum_bit;
	bool				key_bit;
	bool				sequence_num_bit;
	uint32_t			key;
	uint32_t			sequence_num;
	uint32_t			vsid:24;
	uint32_t			flowid:8;
} conf_t;

#endif // __PCAP2GRE_H__
```

```c:pcap2gre.c
#include "pcap2gre.h"

void print_usage(void)
{
	printf("Usage: pcap2gre [pcap in] [pcap out]\n");
	return;
}

const char *gre_name(gretype_e type)
{
	switch (type)
	{
		case GRE_RFC2784:
			return "GRE/RFC2784";
		case GRE_RFC2890:
			return "Key and Sequence Number Extensions to GRE/RFC2890";
		case GRE_NVGRE:
			return "NVGRE draft";
		default:
			assert(false);
			return NULL;
	}
}

bool parse_element(	const char	*buf		,
					const int	type		,
					const char	*findKey	,
					void		*getvalue	)
{
	char key[CONF_SIZE], value[CONF_SIZE];

	if (sscanf(buf, "%s = %s", key, value) != 2)
	{
		if (sscanf(buf, "%s =", key) == 1)
		{
			value[0] = '\0';
		}
		else
		{
			printf("pcap2gre.conf format error. %s\n", buf);
			return false;
		}
	}

	if (strcmp(findKey, key) == 0)
	{
		if (CONF_INT == type)
		{
			*(uint32_t *)getvalue = atoi(value);
			return true;
		}
		else
		{
			snprintf((char *)getvalue, CONF_SIZE, "%s", value);
			return true;
		}
	}
	return false;
}

bool parse_conf(struct sys_conf *conf)
{
	int			ret;
	FILE		*fp;
	char		row[PATH_MAX];
	char		bufs[CONF_SIZE];
	uint32_t	bufi;

	fp = fopen("pcap2gre.conf", "r");
	if (NULL == fp) {
		fprintf(stderr, "pcap2gre.conf\n");
		return false;
	}

	memset(conf, 0x00, sizeof(conf_t));

	while (NULL != fgets(row , sizeof(row) , fp)) {

		if (row[strlen(row) - 1] == '\n') row[strlen(row) - 1] = '\0';
		if (row[0] == '#' || row[0] == '\0') continue;

		if (true == parse_element(row, CONF_INT, "GRE_TYPE", &conf->gre_type)) {
			continue;
		}

		if (true == parse_element(row, CONF_STR, "TUNNEL_IP_SRC", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->src));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "TUNNEL_IP_DST", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->dst));
			continue;
		}

		if (true == parse_element(row, CONF_INT, "CHECKSUM_BIT", &bufi)) {
			if (1 == bufi) conf->checksum_bit = 1;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "KEY_BIT", &bufi)) {
			if (1 == bufi) conf->key_bit = 1;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "SEQUENCE_NUM_BIT", &bufi)) {
			if (1 == bufi) conf->sequence_num_bit = 1;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "KEY", &conf->key)) {
			continue;
		}

		if (true == parse_element(row, CONF_INT, "SEQUENCE_NUMBER", &conf->sequence_num)) {
			continue;
		}

		if (true == parse_element(row, CONF_INT, "VSID", &bufi)) {
			conf->vsid = bufi;
			continue;
		}

		if (true == parse_element(row, CONF_INT, "FLOWID", &bufi)) {
			conf->flowid = bufi;
			continue;
		}

	}

	fclose(fp);
	return true;
}

unsigned short pkt_checksum(unsigned short *data, size_t len, unsigned short add)
{
	unsigned int L_sum = add;
	union	{
				unsigned char	L_8bit[2];
				unsigned short	L_16bit;
	} L_Tail;
	
	 while( len > 1 ){
		L_sum += ntohs(*data++);
		len -= 2;
	}

	if(len == 1){
		L_Tail.L_16bit = 0;
		L_Tail.L_8bit[0] = *(u_int8_t *)data;
		L_sum += ntohs(L_Tail.L_16bit);
	}
	
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);

	return	~(unsigned short)L_sum;
}

int main (int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
{
	FILE		*fp;
	pcap_t		*pd;
	char		ebuf[PCAP_ERRBUF_SIZE];
	char		cap_in[PATH_MAX];
	char		cap_out[PATH_MAX];
	uint32_t	i = 0;
	uint32_t	j = 0;

	struct pcap_file_header	pfhdr;
	struct pcap_pkthdr		phdr_in;
	struct pd_pkthdr		phdr_out;
	const uint8_t			*data;
	const unsigned char		*pkt;

	struct sys_conf		conf;
	char				bufsrc[INET_ADDRSTRLEN];
	char				bufdst[INET_ADDRSTRLEN];

	if (argc > 2) {
		strncpy(cap_in, argv[1], PATH_MAX);
		strncpy(cap_out, argv[2], PATH_MAX);
	} else {
		print_usage();
		exit(EXIT_FAILURE);
	}

	assert(parse_conf(&conf));

	printf("in[%s] out[%s]\n", argv[1], argv[2]);
	printf("GRE[%d:%s]\ntunnel_ip src[%s] dst[%s]\n",
		conf.gre_type, gre_name(conf.gre_type),
		inet_ntop(AF_INET, (const void *)&conf.src, bufsrc, INET_ADDRSTRLEN),
		inet_ntop(AF_INET, (const void *)&conf.dst, bufdst, INET_ADDRSTRLEN));
	printf("checksum_bit[%d] key_bit[%d] sequence_num_bit[%d]\n",
		conf.checksum_bit, conf.key_bit, conf.sequence_num_bit);
	printf("key[%d] sequence_num[%d] vsid[%d] flowid[%d]\n\n",
		conf.key, conf.sequence_num, conf.vsid, conf.flowid);

	memset(&pfhdr, 0x00, sizeof(struct pcap_file_header));
	pfhdr.magic = TCPDUMP_MAGIC;
	pfhdr.version_major = PCAP_VERSION_MAJOR;
	pfhdr.version_minor = PCAP_VERSION_MINOR;
	pfhdr.snaplen       = PCAP_SNAPLEN;
	pfhdr.linktype      = DLT_EN10MB;

	struct ether_h 		*eth_hdr;
	struct ether_addr	smac;

	uint16_t ip_size;
	uint16_t csum;
	uint16_t etype;
	uint16_t *etypep;
	uint16_t elength;
	uint8_t  *head;

	/* Tunnel ipv4 header */
	struct ip	iph_out;
	struct ip	iph_in;
	memset(&iph_out, 0x00, sizeof(struct ip));
	memset(&iph_in, 0x00, sizeof(struct ip));

	iph_out.ip_v   = 4;
	iph_out.ip_hl  = 5;
	iph_out.ip_hl  = 5;
	iph_out.ip_off = 0x0040;
	iph_out.ip_ttl = 0x40;
	iph_out.ip_p   = 0x2f;
	memcpy(&iph_out.ip_src, &conf.src, sizeof(struct in_addr));
	memcpy(&iph_out.ip_dst, &conf.dst, sizeof(struct in_addr));

	iph_in.ip_v   = 4;
	iph_in.ip_hl  = 5;
	iph_in.ip_off = 0x0040;
	iph_in.ip_ttl = 0x40;
	iph_in.ip_p   = 0x2f;
	memcpy(&iph_in.ip_src, &conf.dst, sizeof(struct in_addr));
	memcpy(&iph_in.ip_dst, &conf.src, sizeof(struct in_addr));

	/* GRE header */
	struct gre	gre_hdr;
	uint32_t	opt_cnt  = 0;
	uint32_t	gre_size = 0;
	memset(&gre_hdr, 0x00, sizeof(struct gre));

	uint32_t	vsid;

	switch (conf.gre_type)
	{
		case GRE_RFC2784:
			/* Checksum Present */
			if (conf.checksum_bit) {
				gre_hdr.cflg = 1;
				opt_cnt++;
			}
			break;
		case GRE_RFC2890:
			/* Checksum Present */
			if (conf.checksum_bit) {
				gre_hdr.cflg = 1;
				opt_cnt++;
			}
			/* Key Present */
			if (conf.key_bit) {
				gre_hdr.kflg = 1;
				gre_hdr.opt[opt_cnt].key = htonl(conf.key);
				opt_cnt++;
			}
			/* Sequence Number Present */
			if (conf.sequence_num_bit) {
				gre_hdr.sflg = 1;
				gre_hdr.opt[opt_cnt].seq = htonl(conf.sequence_num);
				opt_cnt++;
			}
			break;
		case GRE_NVGRE:
		default:
			gre_hdr.kflg = 1;
			vsid = htonl(conf.vsid);
			printf("vsid [0x%x] [0x%x]\n\n", conf.vsid, vsid);
			gre_hdr.opt[opt_cnt].vsid = vsid>>8;
			gre_hdr.opt[opt_cnt].flowid = conf.flowid;
			opt_cnt++;
			break;
	}

	gre_size = 4 + (opt_cnt * 4);

	/* pcap open */
	assert(pd = pcap_open_offline(cap_in, ebuf));
	assert(fp = fopen(cap_out, "wb"));

	fwrite(&pfhdr, sizeof(struct pcap_file_header), 1, fp);

	while (1) {

		if ((data = pcap_next(pd, &phdr_in)) != NULL) {
			eth_hdr = (struct ether_h *)data;

			if (!i) {
				memcpy(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr));
			}

			i++;

			printf("[%d] caplen[%d] len[%d] ts[%"PRId64".%"PRId64"] src[%s] dst[%s]\n",
				i, phdr_in.caplen, phdr_in.len, phdr_in.ts.tv_sec, phdr_in.ts.tv_usec,
				ether_ntoa(&eth_hdr->ether_shost), ether_ntoa(&eth_hdr->ether_dhost));

			/* Ether header 読み飛ばし */
			head = (uint8_t *)data;
			elength = 12;

			for (j = 0; j < 2; j++) {
				etype = ntohs(*(uint16_t *)(head + elength));
				printf("\tether_type[0x%x]\n", etype);
		
				switch (etype) {

					case 0x8100:
					case 0x88a8:
					case 0x9100:
					case 0x9200:
					case 0x9300:
						elength += 4;
						break;
			
					case 0x86dd:
						etypep = (uint16_t *)(head + elength);
						*etypep = 0x0008;
						goto loopend;
					default:
						goto loopend;
				}
			}
loopend:
			/* pcap header */
			memset(&phdr_out, 0x00, sizeof(struct pd_pkthdr));
			phdr_out.ts.tv_sec  = (uint32_t)phdr_in.ts.tv_sec;
			phdr_out.ts.tv_usec = (uint32_t)phdr_in.ts.tv_usec;
			phdr_out.caplen  = phdr_in.caplen + 20 + gre_size;
			phdr_out.len     = phdr_in.len + 20 + gre_size;

			fwrite(&phdr_out, sizeof(struct pd_pkthdr), 1, fp);

			/* Ether header */
			elength += 2;
			fwrite(eth_hdr, elength, 1, fp);
			printf("\tether_length[%d]\n", elength);

			ip_size  = 20 + gre_size + (phdr_in.caplen - elength);

			/* in or out 判定 */
			if(0 == memcmp(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr))) {
				/* Tunnel IP header (out) */
				iph_out.ip_len = htons(ip_size);
				iph_out.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_out, 20, 0);
				iph_out.ip_sum = htons(csum);
				fwrite(&iph_out, 20, 1, fp);
			} else {
				/* Tunnel IP header (in) */
				iph_in.ip_len = htons(ip_size);
				iph_in.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_in, 20, 0);
				iph_in.ip_sum = htons(csum);
				fwrite(&iph_in, 20, 1, fp);
			}

			/* GRE header */
			gre_hdr.proto = htons(etype);

			if (gre_hdr.cflg) {
				gre_hdr.opt[0].csum = 0;
				csum = pkt_checksum((unsigned short *)&gre_hdr, gre_size, 0);
				printf("\tcsum[0x%x]\n", csum);
				csum = pkt_checksum((unsigned short *)(head + elength), (phdr_in.caplen - elength), csum);
				printf("\tcsum[0x%x]\n", csum);
				gre_hdr.opt[0].csum = htons(csum);
			}

			fwrite(&gre_hdr, gre_size, 1, fp);

			/* Carrier Protocol */
			fwrite((head + elength), (phdr_in.caplen - elength), 1, fp);
		} else {
			break;
		}
	}

funcend:
	pcap_close(pd);
	fclose(fp);

	exit(EXIT_SUCCESS);
}
```

```c:pcap2gre.conf
# RFC2784 
#
#  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |C|       Reserved0       | Ver |         Protocol Type         |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |      Checksum (optional)      |       Reserved1 (Optional)    |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
# RFC2890  Key and Sequence Number Extensions to GRE
# https://tools.ietf.org/html/rfc2890
#
#  The proposed GRE header will have the following format:
#
#   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |C| |K|S| Reserved0       | Ver |         Protocol Type         |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |      Checksum (optional)      |       Reserved1 (Optional)    |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |                         Key (optional)                        |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |                 Sequence Number (Optional)                    |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
# NVGRE draft
# https://tools.ietf.org/html/draft-sridharan-virtualization-nvgre-07
#
#  GRE Header:
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |0| |1|0|   Reserved0     | Ver |   Protocol Type 0x6558        |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#  |               Virtual Subnet ID (VSID)        |    FlowID     |
#  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#
#
# GRE_TYPE
#   0 : RFC2784
#   1 : RFC2890
#   2 : NVGRE
GRE_TYPE = 0

TUNNEL_IP_SRC = 2.2.2.1
TUNNEL_IP_DST = 2.2.2.2

#CHECKSUM_BIT = 0
CHECKSUM_BIT = 1

KEY_BIT = 0
#KEY_BIT = 1

SEQUENCE_NUM_BIT = 0
#SEQUENCE_NUM_BIT = 1

#KEY = 100
#SEQUENCE_NUMBER = 200
#VSID = 100
#FLOWID = 15
```

---
VXLANヘッダの付与

```c:pcap2vxlan.h
#ifndef __PCAP2VXLAN_H__
#define __PCAP2VXLAN_H__

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <pthread.h>
#include <assert.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <inttypes.h>
#define __STDC_FORMAT_MACROS

#define CONF_INT	0
#define CONF_STR	1
#define CONF_SIZE	1024

#define TCPDUMP_MAGIC		0xa1b2c3d4
#define PCAP_VERSION_MAJOR	2
#define PCAP_VERSION_MINOR	4
#define DLT_EN10MB			1				/* Ethernet (10Mb) */
#define PCAP_SNAPLEN		0xffff

struct pd_timeval {
	uint32_t	tv_sec;
	uint32_t	tv_usec;
};

struct pd_pkthdr {
	struct pd_timeval	ts;
	uint32_t			caplen;
	uint32_t			len;
};

typedef struct ether_h {
	struct ether_addr	ether_dhost;
	struct ether_addr	ether_shost;
	uint16_t			ntag;
}ether_t;

/*
 *  VXLAN Header:
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |R|R|R|R|I|R|R|R|            Reserved                           |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *  |                VXLAN Network Identifier (VNI) |   Reserved    |
 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 */

struct vxlan
{
	#if __BYTE_ORDER == __LITTLE_ENDIAN
		uint32_t	r6_8:3;		/* 6 to 8 R bit */
		uint32_t	i:1;		/* I bit		*/
		uint32_t	r1_4:4;		/* 1 to 4 R bit */
	#endif

	#if __BYTE_ORDER == __BIG_ENDIAN
		uint32_t	r1_4:4;		/* 1 to 4 R bit */
		uint32_t	i:1;		/* I bit		*/
		uint32_t	r6_8:3;		/* 6 to 8 R bit */
	#endif

	uint32_t		pad1:24;

	uint32_t		vni:24;
	uint32_t		pad2:8;
};

typedef struct sys_conf {
	struct ether_addr	src_mac;
	struct ether_addr	dst_mac;
	struct in_addr		src_ip;
	struct in_addr		dst_ip;
	uint32_t			vxlan_port;
	uint32_t			vxlan_vni;
} conf_t;

#endif // __PCAP2VXLAN_H__
```

```c:pcap2vxlan.c
#include "pcap2vxlan.h"

void print_usage(void)
{
	printf("Usage: pcap2vxlan [pcap in] [pcap out]\n");

	return;
}

bool parse_element(	const char	*buf		,
					const int	type		,
					const char	*findKey	,
					void		*getvalue	)
{
	char key[CONF_SIZE], value[CONF_SIZE];

	if (sscanf(buf, "%s = %s", key, value) != 2) {
		if (sscanf(buf, "%s =", key) == 1) {
			value[0] = '\0';
		} else {
			printf("pcap2gre.conf format error. %s\n", buf);
			return false;
		}
	}

	if (strcmp(findKey, key) == 0) {
		if (CONF_INT == type) {
			*(uint32_t *)getvalue = atoi(value);
			return true;
		} else {
			snprintf((char *)getvalue, CONF_SIZE, "%s", value);
			return true;
		}
	}
	return false;
}

bool parse_conf(struct sys_conf *conf)
{
	int			ret;
	FILE		*fp;
	char		row[PATH_MAX];
	char		bufs[CONF_SIZE];
	uint32_t	bufi;

	fp = fopen("pcap2vxlan.conf", "r");
	if (NULL == fp) {
		fprintf(stderr, "pcap2gre.conf\n");
		return false;
	}

	memset(conf, 0x00, sizeof(conf_t));

	while (NULL != fgets(row , sizeof(row) , fp)) {

		if (row[strlen(row) - 1] == '\n') row[strlen(row) - 1] = '\0';
		if (row[0] == '#' || row[0] == '\0') continue;

		if (true == parse_element(row, CONF_STR, "OUTER_SRC_MAC", bufs)) {
			assert(ether_aton_r(bufs, &conf->src_mac));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "OUTER_DST_MAC", bufs)) {
			assert(ether_aton_r(bufs, &conf->dst_mac));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "OUTER_SRC_IP", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->src_ip));
			continue;
		}

		if (true == parse_element(row, CONF_STR, "OUTER_DST_IP", bufs)) {
			assert(inet_pton(AF_INET, bufs, &conf->dst_ip));
			continue;
		}

		if (true == parse_element(row, CONF_INT, "VXLAN_PORT", &conf->vxlan_port)) {
			continue;
		}

		if (true == parse_element(row, CONF_INT, "VXLAN_VNI", &conf->vxlan_vni)) {
			continue;
		}
	}

	fclose(fp);
	return true;
}

unsigned short pkt_checksum(unsigned short *data, size_t len, unsigned short add)
{
	unsigned int L_sum = add;
	union	{
				unsigned char	L_8bit[2];
				unsigned short	L_16bit;
	} L_Tail;
	
	 while( len > 1 ){
		L_sum += ntohs(*data++);
		len -= 2;
	}

	if(len == 1){
		L_Tail.L_16bit = 0;
		L_Tail.L_8bit[0] = *(u_int8_t *)data;
		L_sum += ntohs(L_Tail.L_16bit);
	}
	
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);
	L_sum = (L_sum & 0xffff) + (L_sum >> 16);

	return	~(unsigned short)L_sum;
}

int main (int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
{
	FILE		*fp;
	pcap_t		*pd;
	char		ebuf[PCAP_ERRBUF_SIZE];
	char		cap_in[PATH_MAX];
	char		cap_out[PATH_MAX];
	uint32_t	i = 0;

	struct pcap_file_header	pfhdr;
	struct pcap_pkthdr		phdr_in;
	struct pd_pkthdr		phdr_out;
	const uint8_t			*data;
	const unsigned char		*pkt;

	struct sys_conf		conf;
	char				bufsrc[INET_ADDRSTRLEN];
	char				bufdst[INET_ADDRSTRLEN];


	if (argc > 2) {
		strncpy(cap_in, argv[1], PATH_MAX);
		strncpy(cap_out, argv[2], PATH_MAX);
	} else {
		print_usage();
		exit(EXIT_FAILURE);
	}

	assert(parse_conf(&conf));

	printf("in[%s] out[%s]\n", argv[1], argv[2]);
	printf("mac_src[%s] mac_dst[%s]\n",
		ether_ntoa(&conf.src_mac), ether_ntoa(&conf.dst_mac));
	printf("ip_src[%s] ip_dst[%s]\n",
		inet_ntop(AF_INET, (const void *)&conf.src_ip, bufsrc, INET_ADDRSTRLEN),
		inet_ntop(AF_INET, (const void *)&conf.dst_ip, bufdst, INET_ADDRSTRLEN));
	printf("vxlan_port[%d] vxlan_vni[%d]\n\n", conf.vxlan_port, conf.vxlan_vni);

	memset(&pfhdr, 0x00, sizeof(struct pcap_file_header));
	pfhdr.magic = TCPDUMP_MAGIC;
	pfhdr.version_major = PCAP_VERSION_MAJOR;
	pfhdr.version_minor = PCAP_VERSION_MINOR;
	pfhdr.snaplen       = PCAP_SNAPLEN;
	pfhdr.linktype      = DLT_EN10MB;

	/* ether */
	struct ether_h	ether_out;
	struct ether_h	ether_in;
	memset(&ether_out, 0x00, sizeof(struct ether_h));
	memset(&ether_in, 0x00, sizeof(struct ether_h));

	memcpy(&ether_out.ether_shost, &conf.src_mac, sizeof(struct ether_addr));
	memcpy(&ether_out.ether_dhost, &conf.dst_mac, sizeof(struct ether_addr));
	ether_out.ntag = 0x0008;

	memcpy(&ether_in.ether_shost, &conf.dst_mac, sizeof(struct ether_addr));
	memcpy(&ether_in.ether_dhost, &conf.src_mac, sizeof(struct ether_addr));
	ether_in.ntag = 0x0008;

	/* ipv4 */
	struct ip	iph_out;
	struct ip	iph_in;
	memset(&iph_out, 0x00, sizeof(struct ip));
	memset(&iph_in, 0x00, sizeof(struct ip));

	iph_out.ip_v   = 4;
	iph_out.ip_hl  = 5;
	iph_out.ip_hl  = 5;
	iph_out.ip_off = 0x0040;
	iph_out.ip_ttl = 0x40;
	iph_out.ip_p   = 0x11;
	memcpy(&iph_out.ip_src, &conf.src_ip, sizeof(struct in_addr));
	memcpy(&iph_out.ip_dst, &conf.dst_ip, sizeof(struct in_addr));

	iph_in.ip_v   = 4;
	iph_in.ip_hl  = 5;
	iph_in.ip_off = 0x0040;
	iph_in.ip_ttl = 0x40;
	iph_in.ip_p   = 0x11;
	memcpy(&iph_in.ip_src, &conf.dst_ip, sizeof(struct in_addr));
	memcpy(&iph_in.ip_dst, &conf.src_ip, sizeof(struct in_addr));

	/* udp */
	struct udphdr	udph_out;
	struct udphdr	udph_in;
	memset(&udph_out, 0x00, sizeof(struct udphdr));
	memset(&udph_in, 0x00, sizeof(struct udphdr));

	uint16_t	dst_port;
	dst_port = conf.vxlan_port;

	udph_out.dest = htons(dst_port);
	udph_in.dest  = htons(dst_port);

	/* vxlan */
	struct vxlan	vxlanh;
	memset(&vxlanh, 0x00, sizeof(struct vxlan));

	vxlanh.i = 1;

	uint32_t	vni;
	vni = htonl(conf.vxlan_vni);
	vxlanh.vni = vni>>8;

	/* pcap open */
	assert(pd = pcap_open_offline(cap_in, ebuf));
	assert(fp = fopen(cap_out, "wb"));

	fwrite(&pfhdr, sizeof(struct pcap_file_header), 1, fp);

	struct ether_h 		*eth_hdr;
	struct ether_addr	smac;
	uint16_t			ip_size;
	uint16_t			udp_size;
	uint16_t			csum;

	while (1) {

		if ((data = pcap_next(pd, &phdr_in)) != NULL) {
			eth_hdr = (struct ether_h *)data;

			if (!i) {
				memcpy(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr));
			}

			i++;

			printf("[%d] caplen[%d] len[%d] ts[%"PRId64".%"PRId64"] src[%s] dst[%s]\n",
				i, phdr_in.caplen, phdr_in.len, phdr_in.ts.tv_sec, phdr_in.ts.tv_usec,
				ether_ntoa(&eth_hdr->ether_shost), ether_ntoa(&eth_hdr->ether_dhost));

			memset(&phdr_out, 0x00, sizeof(struct pd_pkthdr));
			phdr_out.ts.tv_sec  = (uint32_t)phdr_in.ts.tv_sec;
			phdr_out.ts.tv_usec = (uint32_t)phdr_in.ts.tv_usec;
			phdr_out.caplen  = phdr_in.caplen + 50;
			phdr_out.len     = phdr_in.len + 50;

			fwrite(&phdr_out, sizeof(struct pd_pkthdr), 1, fp);

			ip_size  = 36 + phdr_in.caplen;
			udp_size = 16 + phdr_in.caplen;

			/* in or out 判定 */
			if(0 == memcmp(&smac, &eth_hdr->ether_shost, sizeof(struct ether_addr))) {

				udph_out.source = pkt_checksum((unsigned short *)&eth_hdr, sizeof(struct ether_h), 0);
				iph_out.ip_len = htons(ip_size);
				udph_out.len   = htons(udp_size);
				iph_out.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_out, 20, 0);
				iph_out.ip_sum = htons(csum);

				fwrite(&ether_out, 14, 1, fp);
				fwrite(&iph_out, 20, 1, fp);
				fwrite(&udph_out, 8, 1, fp);
				fwrite(&vxlanh, 8, 1, fp);
			} else {
				udph_in.source = pkt_checksum((unsigned short *)&eth_hdr, sizeof(struct ether_h), 0);
				iph_in.ip_len = htons(ip_size);
				udph_in.len   = htons(udp_size);
				iph_in.ip_sum = 0;
				csum = pkt_checksum((unsigned short *)&iph_in, 20, 0);
				iph_in.ip_sum = htons(csum);

				fwrite(&ether_in, 14, 1, fp);
				fwrite(&iph_in, 20, 1, fp);
				fwrite(&udph_in, 8, 1, fp);
				fwrite(&vxlanh, 8, 1, fp);
			}

			fwrite(data, phdr_in.caplen, 1, fp);

		} else {
			break;
		}
	}

	pcap_close(pd);
	fclose(fp);

	exit(EXIT_SUCCESS);
}
```

```c:pcap2vxlan.conf
# VXLAN Header:
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |R|R|R|R|I|R|R|R|            Reserved                           |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |                VXLAN Network Identifier (VNI) |   Reserved    |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#

OUTER_SRC_MAC = 10:10:10:10:10:10
OUTER_DST_MAC = 20:20:20:20:20:20

OUTER_SRC_IP = 3.3.3.1
OUTER_DST_IP = 3.3.3.1

VXLAN_PORT = 4789
VXLAN_VNI = 100
```